import java.util.*;
/*
To avoid duplicates with with skips we need to find start nodes that are as close as possible to the root
If it is not possible to find a node for a prefix with a given amount of errors, ONLY then we try to apply skips
*/
class Solution {
static final int ERRORS_ALLOWED = 2;
static class TrieNode {
Map
<Character, TrieNode
> map
= new HashMap
<>(); }
TrieNode root = new TrieNode();
public Solution(List<String> dictionary) {
for (String word
: dictionary
) { TrieNode cur = root;
for (char c : word.toCharArray()) {
if (!cur.map.containsKey(c)) {
TrieNode next = new TrieNode();
cur.map.put(c, next);
}
cur = cur.map.get(c);
}
cur.word = word;
}
}
List
<String
> getWords
(String prefix
) { List<TrieNode> startNodes = getStartNodes(root, prefix, 0, 0, new HashSet<>());
List<String> words = new ArrayList<>();
for (TrieNode node : startNodes) {
words.addAll(getAllWords(node));
}
return words;
}
private List<String> getAllWords(TrieNode node) {
List<String> words = new ArrayList<>();
if (node.word != null) {
words.add(node.word);
}
for (TrieNode next : node.map.values()) {
words.addAll(getAllWords(next));
}
return words;
}
private List
<TrieNode
> getStartNodes
(TrieNode node,
String prefix,
int ind,
int errors, Set
<TrieNode
> visited
) { List<TrieNode> res = new ArrayList<>();
if (visited.contains(node)) {
return res;
}
if (ind >= prefix.length()) {
visited.addAll(traverse(node));
}
char charOfPrefix = prefix.charAt(ind);
if (node.map.containsKey(charOfPrefix)) {
res.addAll(getStartNodes(node.map.get(charOfPrefix), prefix, ind + 1, errors, visited));
}
if (errors < ERRORS_ALLOWED) {
for (char c : node.map.keySet()) {
if (node.map.containsKey(charOfPrefix) && c == charOfPrefix) {
continue;
}
List<TrieNode> withPrefixShift = getStartNodes(node.map.get(c), prefix, ind + 1, errors + 1, visited);
res.addAll(withPrefixShift);
res.addAll(getStartNodes(node.map.get(c), prefix, ind, errors + 1, visited));
}
}
return res;
}
private List<TrieNode> traverse(TrieNode node) {
List<TrieNode> treeNodes = new ArrayList<>();
treeNodes.add(node);
for (TrieNode next : node.map.values()) {
treeNodes.addAll(traverse(next));
}
return treeNodes;
}
public static void main
(String[] args
) { testSkipNotNeeded();
testSkipNeeded();
}
static void testSkipNotNeeded() {
List
<String
> dictionary
= List.
of("abc",
"doecm",
"abzd",
"azfs",
"kzcs"); Solution solution = new Solution(dictionary);
List<String> words = solution.getWords("abc");
}
static void testSkipNeeded() {
List
<String
> dictionary
= List.
of("abzd",
"helloaaa",
"heoaabb",
"heloaabb"); Solution solution = new Solution(dictionary);
List<String> words = solution.getWords("heoaa");
}
}
aW1wb3J0IGphdmEudXRpbC4qOwoKLyoKVG8gYXZvaWQgZHVwbGljYXRlcyB3aXRoIHdpdGggc2tpcHMgd2UgbmVlZCB0byBmaW5kIHN0YXJ0IG5vZGVzIHRoYXQgYXJlIGFzIGNsb3NlIGFzIHBvc3NpYmxlIHRvIHRoZSByb290CklmIGl0IGlzIG5vdCBwb3NzaWJsZSB0byBmaW5kIGEgbm9kZSBmb3IgYSBwcmVmaXggd2l0aCBhIGdpdmVuIGFtb3VudCBvZiBlcnJvcnMsIE9OTFkgdGhlbiB3ZSB0cnkgdG8gYXBwbHkgc2tpcHMKICovCgpjbGFzcyBTb2x1dGlvbiB7CgogICAgc3RhdGljIGZpbmFsIGludCBFUlJPUlNfQUxMT1dFRCA9IDI7CgogICAgc3RhdGljIGNsYXNzIFRyaWVOb2RlIHsKICAgICAgICBNYXA8Q2hhcmFjdGVyLCBUcmllTm9kZT4gbWFwID0gbmV3IEhhc2hNYXA8PigpOwogICAgICAgIFN0cmluZyB3b3JkID0gbnVsbDsKICAgIH0KCiAgICBUcmllTm9kZSByb290ID0gbmV3IFRyaWVOb2RlKCk7CgogICAgcHVibGljIFNvbHV0aW9uKExpc3Q8U3RyaW5nPiBkaWN0aW9uYXJ5KSB7CiAgICAgICAgZm9yIChTdHJpbmcgd29yZCA6IGRpY3Rpb25hcnkpIHsKICAgICAgICAgICAgVHJpZU5vZGUgY3VyID0gcm9vdDsKICAgICAgICAgICAgZm9yIChjaGFyIGMgOiB3b3JkLnRvQ2hhckFycmF5KCkpIHsKICAgICAgICAgICAgICAgIGlmICghY3VyLm1hcC5jb250YWluc0tleShjKSkgewogICAgICAgICAgICAgICAgICAgIFRyaWVOb2RlIG5leHQgPSBuZXcgVHJpZU5vZGUoKTsKICAgICAgICAgICAgICAgICAgICBjdXIubWFwLnB1dChjLCBuZXh0KTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGN1ciA9IGN1ci5tYXAuZ2V0KGMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGN1ci53b3JkID0gd29yZDsKICAgICAgICB9CiAgICB9CgogICAgTGlzdDxTdHJpbmc+IGdldFdvcmRzKFN0cmluZyBwcmVmaXgpIHsKICAgICAgICBMaXN0PFRyaWVOb2RlPiBzdGFydE5vZGVzID0gZ2V0U3RhcnROb2Rlcyhyb290LCBwcmVmaXgsIDAsIDAsIG5ldyBIYXNoU2V0PD4oKSk7CiAgICAgICAgTGlzdDxTdHJpbmc+IHdvcmRzID0gbmV3IEFycmF5TGlzdDw+KCk7CiAgICAgICAgZm9yIChUcmllTm9kZSBub2RlIDogc3RhcnROb2RlcykgewogICAgICAgICAgICB3b3Jkcy5hZGRBbGwoZ2V0QWxsV29yZHMobm9kZSkpOwogICAgICAgIH0KICAgICAgICByZXR1cm4gd29yZHM7CiAgICB9CgogICAgcHJpdmF0ZSBMaXN0PFN0cmluZz4gZ2V0QWxsV29yZHMoVHJpZU5vZGUgbm9kZSkgewogICAgICAgIExpc3Q8U3RyaW5nPiB3b3JkcyA9IG5ldyBBcnJheUxpc3Q8PigpOwogICAgICAgIGlmIChub2RlLndvcmQgIT0gbnVsbCkgewogICAgICAgICAgICB3b3Jkcy5hZGQobm9kZS53b3JkKTsKICAgICAgICB9CiAgICAgICAgZm9yIChUcmllTm9kZSBuZXh0IDogbm9kZS5tYXAudmFsdWVzKCkpIHsKICAgICAgICAgICAgd29yZHMuYWRkQWxsKGdldEFsbFdvcmRzKG5leHQpKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHdvcmRzOwogICAgfQoKICAgIHByaXZhdGUgTGlzdDxUcmllTm9kZT4gZ2V0U3RhcnROb2RlcyhUcmllTm9kZSBub2RlLCBTdHJpbmcgcHJlZml4LCBpbnQgaW5kLCBpbnQgZXJyb3JzLCBTZXQ8VHJpZU5vZGU+IHZpc2l0ZWQpIHsKICAgICAgICBMaXN0PFRyaWVOb2RlPiByZXMgPSBuZXcgQXJyYXlMaXN0PD4oKTsKICAgICAgICBpZiAodmlzaXRlZC5jb250YWlucyhub2RlKSkgewogICAgICAgICAgICByZXR1cm4gcmVzOwogICAgICAgIH0KICAgICAgICBpZiAoaW5kID49IHByZWZpeC5sZW5ndGgoKSkgewogICAgICAgICAgICB2aXNpdGVkLmFkZEFsbCh0cmF2ZXJzZShub2RlKSk7CiAgICAgICAgICAgIHJldHVybiBDb2xsZWN0aW9ucy5zaW5nbGV0b25MaXN0KG5vZGUpOwogICAgICAgIH0KICAgICAgICBjaGFyIGNoYXJPZlByZWZpeCA9IHByZWZpeC5jaGFyQXQoaW5kKTsKICAgICAgICBpZiAobm9kZS5tYXAuY29udGFpbnNLZXkoY2hhck9mUHJlZml4KSkgewogICAgICAgICAgICByZXMuYWRkQWxsKGdldFN0YXJ0Tm9kZXMobm9kZS5tYXAuZ2V0KGNoYXJPZlByZWZpeCksIHByZWZpeCwgaW5kICsgMSwgZXJyb3JzLCB2aXNpdGVkKSk7CiAgICAgICAgfQogICAgICAgIGlmIChlcnJvcnMgPCBFUlJPUlNfQUxMT1dFRCkgewogICAgICAgICAgICBmb3IgKGNoYXIgYyA6IG5vZGUubWFwLmtleVNldCgpKSB7CiAgICAgICAgICAgICAgICBpZiAobm9kZS5tYXAuY29udGFpbnNLZXkoY2hhck9mUHJlZml4KSAmJiBjID09IGNoYXJPZlByZWZpeCkgewogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgTGlzdDxUcmllTm9kZT4gd2l0aFByZWZpeFNoaWZ0ID0gZ2V0U3RhcnROb2Rlcyhub2RlLm1hcC5nZXQoYyksIHByZWZpeCwgaW5kICsgMSwgZXJyb3JzICsgMSwgdmlzaXRlZCk7CiAgICAgICAgICAgICAgICByZXMuYWRkQWxsKHdpdGhQcmVmaXhTaGlmdCk7CiAgICAgICAgICAgICAgICByZXMuYWRkQWxsKGdldFN0YXJ0Tm9kZXMobm9kZS5tYXAuZ2V0KGMpLCBwcmVmaXgsIGluZCwgZXJyb3JzICsgMSwgdmlzaXRlZCkpOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiByZXM7CiAgICB9CgogICAgcHJpdmF0ZSBMaXN0PFRyaWVOb2RlPiB0cmF2ZXJzZShUcmllTm9kZSBub2RlKSB7CiAgICAgICAgTGlzdDxUcmllTm9kZT4gdHJlZU5vZGVzID0gbmV3IEFycmF5TGlzdDw+KCk7CiAgICAgICAgdHJlZU5vZGVzLmFkZChub2RlKTsKCiAgICAgICAgZm9yIChUcmllTm9kZSBuZXh0IDogbm9kZS5tYXAudmFsdWVzKCkpIHsKICAgICAgICAgICAgdHJlZU5vZGVzLmFkZEFsbCh0cmF2ZXJzZShuZXh0KSk7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gdHJlZU5vZGVzOwogICAgfQoKICAgIHB1YmxpYyBzdGF0aWMgdm9pZCBtYWluKFN0cmluZ1tdIGFyZ3MpIHsKICAgICAgICB0ZXN0U2tpcE5vdE5lZWRlZCgpOwogICAgICAgIHRlc3RTa2lwTmVlZGVkKCk7CiAgICB9CgoKICAgIHN0YXRpYyB2b2lkIHRlc3RTa2lwTm90TmVlZGVkKCkgewogICAgICAgIExpc3Q8U3RyaW5nPiBkaWN0aW9uYXJ5ID0gTGlzdC5vZigiYWJjIiwgImRvZWNtIiwgImFiemQiLCAiYXpmcyIsICJremNzIik7CiAgICAgICAgU29sdXRpb24gc29sdXRpb24gPSBuZXcgU29sdXRpb24oZGljdGlvbmFyeSk7CiAgICAgICAgTGlzdDxTdHJpbmc+IHdvcmRzID0gc29sdXRpb24uZ2V0V29yZHMoImFiYyIpOwogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbih3b3Jkcyk7CiAgICB9CiAgICAKICAgIHN0YXRpYyB2b2lkIHRlc3RTa2lwTmVlZGVkKCkgewogICAgICAgIExpc3Q8U3RyaW5nPiBkaWN0aW9uYXJ5ID0gTGlzdC5vZigiYWJ6ZCIsICJoZWxsb2FhYSIsICJoZW9hYWJiIiwgImhlbG9hYWJiIik7CiAgICAgICAgU29sdXRpb24gc29sdXRpb24gPSBuZXcgU29sdXRpb24oZGljdGlvbmFyeSk7CiAgICAgICAgTGlzdDxTdHJpbmc+IHdvcmRzID0gc29sdXRpb24uZ2V0V29yZHMoImhlb2FhIik7CiAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKHdvcmRzKTsKICAgIH0KfQo=