Design a special dictionary that searches the words in it by a prefix and a suffix.
Implement the WordFilter class:
WordFilter(string[] words) Initializes the object with the words in the dictionary.
f(string pref, string suff) Returns the index of the word in the dictionary, which has the prefix pref and the suffix suff. If there is more than one valid index, return the largest of them. If there is no such word in the dictionary, return -1.
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 <= 104
1 <= words[i].length <= 7
1 <= pref.length, suff.length <= 7
words[i], pref and suff consist of lowercase English letters only.
At most 104 calls will be made to the function f.
Solutions
Solution 1
Thinking
Return the largest index whose word has a given prefix and suffix. Words are at most length \(7\), so every prefix–suffix pair of every word can be stored, and a query is \(O(1)\).
Later words overwrite earlier ones, so the map keeps the largest index. Empty prefix and suffix are stored as well.
Keys are \((pref,suff)\). \(\textit{f}\) looks up or returns \(-1\).
1 2 3 4 5 6 7 8 9101112131415161718
classWordFilter:def__init__(self,words:List[str]):self.d={}fork,winenumerate(words):n=len(w)foriinrange(n+1):a=w[:i]forjinrange(n+1):b=w[j:]self.d[(a,b)]=kdeff(self,pref:str,suff:str)->int:returnself.d.get((pref,suff),-1)# Your WordFilter object will be instantiated and called as such:# obj = WordFilter(words)# param_1 = obj.f(pref,suff)
classWordFilter{privateMap<String,Integer>d=newHashMap<>();publicWordFilter(String[]words){for(intk=0;k<words.length;++k){Stringw=words[k];intn=w.length();for(inti=0;i<=n;++i){Stringa=w.substring(0,i);for(intj=0;j<=n;++j){Stringb=w.substring(j);d.put(a+"."+b,k);}}}}publicintf(Stringpref,Stringsuff){returnd.getOrDefault(pref+"."+suff,-1);}}/** * Your WordFilter object will be instantiated and called as such: * WordFilter obj = new WordFilter(words); * int param_1 = obj.f(pref,suff); */
classWordFilter{public:unordered_map<string,int>d;WordFilter(vector<string>&words){for(intk=0;k<words.size();++k){stringw=words[k];intn=w.size();for(inti=0;i<=n;++i){stringa=w.substr(0,i);for(intj=0;j<=n;++j){stringb=w.substr(j,n-j);d[a+"."+b]=k;}}}}intf(stringpref,stringsuff){stringkey=pref+"."+suff;if(d.count(key))returnd[key];return-1;}};/** * Your WordFilter object will be instantiated and called as such: * WordFilter* obj = new WordFilter(words); * int param_1 = obj->f(pref,suff); */
typeWordFilterstruct{dmap[string]int}funcConstructor(words[]string)WordFilter{d:=map[string]int{}fork,w:=rangewords{n:=len(w)fori:=0;i<=n;i++{a:=w[:i]forj:=0;j<=n;j++{b:=w[j:]d[a+"."+b]=k}}}returnWordFilter{d}}func(this*WordFilter)F(prefstring,suffstring)int{ifv,ok:=this.d[pref+"."+suff];ok{returnv}return-1}/** * Your WordFilter object will be instantiated and called as such: * obj := Constructor(words); * param_1 := obj.F(pref,suff); */
Solution 2
Thinking
Solution 1 stores every prefix–suffix pair. Two tries—one on the word, one on the reverse—record the indices that pass each node.
A query fetches the two index lists (increasing by insert order) and walks them from the right for the largest common index.
Lists stay short because words are short, without materializing every pair.
classTrie:def__init__(self):self.children=[None]*26self.indexes=[]definsert(self,word,i):node=selfforcinword:idx=ord(c)-ord("a")ifnode.children[idx]isNone:node.children[idx]=Trie()node=node.children[idx]node.indexes.append(i)defsearch(self,pref):node=selfforcinpref:idx=ord(c)-ord("a")ifnode.children[idx]isNone:return[]node=node.children[idx]returnnode.indexesclassWordFilter:def__init__(self,words:List[str]):self.p=Trie()self.s=Trie()fori,winenumerate(words):self.p.insert(w,i)self.s.insert(w[::-1],i)deff(self,pref:str,suff:str)->int:a=self.p.search(pref)b=self.s.search(suff[::-1])ifnotaornotb:return-1i,j=len(a)-1,len(b)-1while~iand~j:ifa[i]==b[j]:returna[i]ifa[i]>b[j]:i-=1else:j-=1return-1# Your WordFilter object will be instantiated and called as such:# obj = WordFilter(words)# param_1 = obj.f(pref,suff)
classTrie{Trie[]children=newTrie[26];List<Integer>indexes=newArrayList<>();voidinsert(Stringword,inti){Trienode=this;for(charc:word.toCharArray()){c-='a';if(node.children[c]==null){node.children[c]=newTrie();}node=node.children[c];node.indexes.add(i);}}List<Integer>search(Stringpref){Trienode=this;for(charc:pref.toCharArray()){c-='a';if(node.children[c]==null){returnCollections.emptyList();}node=node.children[c];}returnnode.indexes;}}classWordFilter{privateTriep=newTrie();privateTries=newTrie();publicWordFilter(String[]words){for(inti=0;i<words.length;++i){Stringw=words[i];p.insert(w,i);s.insert(newStringBuilder(w).reverse().toString(),i);}}publicintf(Stringpref,Stringsuff){suff=newStringBuilder(suff).reverse().toString();List<Integer>a=p.search(pref);List<Integer>b=s.search(suff);if(a.isEmpty()||b.isEmpty()){return-1;}inti=a.size()-1,j=b.size()-1;while(i>=0&&j>=0){intc=a.get(i),d=b.get(j);if(c==d){returnc;}if(c>d){--i;}else{--j;}}return-1;}}/** * Your WordFilter object will be instantiated and called as such: * WordFilter obj = new WordFilter(words); * int param_1 = obj.f(pref,suff); */
typeTriestruct{children[26]*Trieindexes[]int}funcnewTrie()*Trie{return&Trie{indexes:[]int{}}}func(this*Trie)insert(wordstring,iint){node:=thisfor_,c:=rangeword{idx:=c-'a'ifnode.children[idx]==nil{node.children[idx]=newTrie()}node=node.children[idx]node.indexes=append(node.indexes,i)}}func(this*Trie)search(prefstring)[]int{node:=thisfor_,c:=rangepref{idx:=c-'a'ifnode.children[idx]==nil{return[]int{}}node=node.children[idx]}returnnode.indexes}typeWordFilterstruct{p*Tries*Trie}funcConstructor(words[]string)WordFilter{p:=newTrie()s:=newTrie()fori,w:=rangewords{p.insert(w,i)s.insert(reverse(w),i)}returnWordFilter{p,s}}func(this*WordFilter)F(prefstring,suffstring)int{a:=this.p.search(pref)b:=this.s.search(reverse(suff))iflen(a)==0||len(b)==0{return-1}i,j:=len(a)-1,len(b)-1fori>=0&&j>=0{ifa[i]==b[j]{returna[i]}ifa[i]>b[j]{i--}else{j--}}return-1}funcreverse(wstring)string{ww:=[]byte(w)fori,j:=0,len(w)-1;i<j;i++{ww[i],ww[j]=ww[j],ww[i]j--}returnstring(ww)}/** * Your WordFilter object will be instantiated and called as such: * obj := Constructor(words); * param_1 := obj.F(pref,suff); */