745. Prefix and Suffix Search
Problem Description
Design a special dictionary that searches words by prefixes and suffixes.
Implement the WordFilter class:
- WordFilter(string[] words) Initializes the object with the words in the dictionary.
- f(string prefix, string suffix) Returns the index of the word in the dictionary, which has the prefix prefix and the suffix suffix. If there is more than one valid index, return the largest of them. If no such word exists, return -1.
Examples:
Example 1:
Input
["WordFilter", "f"]
[[["apple"]], ["a", "e"]]
Output
[null, 0]
Explanation
WordFilter wordFilter = new WordFilter(["apple"]);
wordFilter.f("a", "e"); // return 0, because the word at index 0 has prefix "a" and suffix "e".
Constraints:
- 1 ≤ words.length ≤ 15000
- 1 ≤ words[i].length ≤ 10
- 1 ≤ prefix.length, suffix.length ≤ 10
- words[i], prefix and suffix consist of lower-case English letters only.
- At most 15000 calls will be made to the function f.
Python Solution
class WordFilter:
def __init__(self, words: List[str]):
self.trie = {}
for idx, word in enumerate(words):
word = word + '#' + word
for i in range(len(word)):
curr = self.trie
curr['index'] = idx
for j in range(i, len(word)):
c = word[j]
if c not in curr:
curr[c] = {}
curr = curr[c]
curr['index'] = idx
def f(self, prefix: str, suffix: str) -> int:
curr = self.trie
search = suffix + '#' + prefix
for c in search:
if c not in curr:
return -1
curr = curr[c]
return curr['index']
Implementation Notes:
- Uses a trie data structure to store words with their prefixes and suffixes
- Time complexity: O(N * L^2) for initialization, O(P + S) for search where N is number of words, L is max word length, P is prefix length, and S is suffix length
- Space complexity: O(N * L^2) for storing the trie
Java Solution
class WordFilter {
private TrieNode root;
public WordFilter(String[] words) {
root = new TrieNode();
for (int i = 0; i < words.length; i++) {
String word = words[i];
String wrapped = word + "#" + word;
for (int j = 0; j < word.length(); j++) {
TrieNode curr = root;
curr.index = i;
for (int k = j; k < wrapped.length(); k++) {
char c = wrapped.charAt(k);
curr.children.putIfAbsent(c, new TrieNode());
curr = curr.children.get(c);
curr.index = i;
}
}
}
}
public int f(String prefix, String suffix) {
TrieNode curr = root;
String search = suffix + "#" + prefix;
for (char c : search.toCharArray()) {
if (!curr.children.containsKey(c)) {
return -1;
}
curr = curr.children.get(c);
}
return curr.index;
}
private class TrieNode {
Map children;
int index;
TrieNode() {
children = new HashMap<>();
index = -1;
}
}
}
C++ Solution
class WordFilter {
private:
struct TrieNode {
unordered_map children;
int index;
TrieNode() : index(-1) {}
};
TrieNode* root;
public:
WordFilter(vector& words) {
root = new TrieNode();
for (int i = 0; i < words.size(); i++) {
string word = words[i];
string wrapped = word + "#" + word;
for (int j = 0; j < word.length(); j++) {
TrieNode* curr = root;
curr->index = i;
for (int k = j; k < wrapped.length(); k++) {
char c = wrapped[k];
if (!curr->children.count(c)) {
curr->children[c] = new TrieNode();
}
curr = curr->children[c];
curr->index = i;
}
}
}
}
int f(string prefix, string suffix) {
TrieNode* curr = root;
string search = suffix + "#" + prefix;
for (char c : search) {
if (!curr->children.count(c)) {
return -1;
}
curr = curr->children[c];
}
return curr->index;
}
};
JavaScript Solution
class WordFilter {
constructor(words) {
this.trie = {};
for (let i = 0; i < words.length; i++) {
const word = words[i];
const wrapped = word + '#' + word;
for (let j = 0; j < word.length; j++) {
let curr = this.trie;
curr.index = i;
for (let k = j; k < wrapped.length; k++) {
const c = wrapped[k];
if (!curr[c]) {
curr[c] = {};
}
curr = curr[c];
curr.index = i;
}
}
}
}
f(prefix, suffix) {
let curr = this.trie;
const search = suffix + '#' + prefix;
for (const c of search) {
if (!curr[c]) {
return -1;
}
curr = curr[c];
}
return curr.index;
}
}
C# Solution
public class WordFilter {
private class TrieNode {
public Dictionary children;
public int index;
public TrieNode() {
children = new Dictionary();
index = -1;
}
}
private TrieNode root;
public WordFilter(string[] words) {
root = new TrieNode();
for (int i = 0; i < words.Length; i++) {
string word = words[i];
string wrapped = word + "#" + word;
for (int j = 0; j < word.Length; j++) {
TrieNode curr = root;
curr.index = i;
for (int k = j; k < wrapped.Length; k++) {
char c = wrapped[k];
if (!curr.children.ContainsKey(c)) {
curr.children[c] = new TrieNode();
}
curr = curr.children[c];
curr.index = i;
}
}
}
}
public int F(string prefix, string suffix) {
TrieNode curr = root;
string search = suffix + "#" + prefix;
foreach (char c in search) {
if (!curr.children.ContainsKey(c)) {
return -1;
}
curr = curr.children[c];
}
return curr.index;
}
}
Implementation Notes:
- Uses a trie data structure to store words with their prefixes and suffixes
- Time complexity: O(N * L^2) for initialization, O(P + S) for search where N is number of words, L is max word length, P is prefix length, and S is suffix length
- Space complexity: O(N * L^2) for storing the trie