添加与搜索单词
1.题目内容
请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。
实现词典类 WordDictionary :
WordDictionary() 初始化词典对象
void addWord(word) 将 word 添加到数据结构中,之后可以对它进行匹配
bool search(word) 如果数据结构中存在字符串与 word 匹配,则返回 true ;否则,返回 false 。word 中可能包含一些 '.' ,每个 . 都可以表示任何一个字母。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 输入: ["WordDictionary","addWord","addWord","addWord","search","search","search","search"] [[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]] 输出: [null,null,null,null,false,true,true,true]
解释: WordDictionary wordDictionary = new WordDictionary(); wordDictionary.addWord("bad"); wordDictionary.addWord("dad"); wordDictionary.addWord("mad"); wordDictionary.search("pad"); // 返回 False wordDictionary.search("bad"); // 返回 True wordDictionary.search(".ad"); // 返回 True wordDictionary.search("b.."); // 返回 True
|
提示:
1 <= word.length <= 25
addWord 中的 word 由小写英文字母组成
search 中的 word 由 ‘.’ 或小写英文字母组成
- 最多调用
104 次 addWord 和 search
2.解法
字典树
思路及算法
根据题意,WordDictionary 类需要支持添加单词和搜索单词的操作,可以使用字典树实现。
对于添加单词,将单词添加到字典树中即可。
对于搜索单词,从字典树的根结点开始搜索。由于待搜索的单词可能包含点号,因此在搜索过程中需要考虑点号的处理。对于当前字符是字母和点号的情况,分别按照如下方式处理:
重复上述步骤,直到返回 false\text{false}false 或搜索完给定单词的最后一个字符。
如果搜索完给定的单词的最后一个字符,则当搜索到的最后一个结点的 isEnd 为 true 时,给定的单词存在。
特别地,当搜索到点号时,只要存在一个非空子结点可以搜索到给定的单词,即返回 true。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| //Java class WordDictionary { private Trie root;
public WordDictionary() { root = new Trie(); } public void addWord(String word) { root.insert(word); } public boolean search(String word) { return dfs(word, 0, root); }
private boolean dfs(String word, int index, Trie node) { if (index == word.length()) { return node.isEnd(); } char ch = word.charAt(index); if (Character.isLetter(ch)) { int childIndex = ch - 'a'; Trie child = node.getChildren()[childIndex]; if (child != null && dfs(word, index + 1, child)) { return true; } } else { for (int i = 0; i < 26; i++) { Trie child = node.getChildren()[i]; if (child != null && dfs(word, index + 1, child)) { return true; } } } return false; } }
class Trie { private Trie[] children; private boolean isEnd;
public Trie() { children = new Trie[26]; isEnd = false; } public void insert(String word) { Trie node = this; for (int i = 0; i < word.length(); i++) { char ch = word.charAt(i); int index = ch - 'a'; if (node.children[index] == null) { node.children[index] = new Trie(); } node = node.children[index]; } node.isEnd = true; }
public Trie[] getChildren() { return children; }
public boolean isEnd() { return isEnd; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| //C++ struct TrieNode{ vector<TrieNode *> child; bool isEnd; TrieNode() { this->child = vector<TrieNode *>(26,nullptr); this->isEnd = false; } };
void insert(TrieNode * root, const string & word) { TrieNode * node = root; for (auto c : word) { if (node->child[c - 'a'] == nullptr) { node->child[c - 'a'] = new TrieNode(); } node = node->child[c - 'a']; } node->isEnd = true; }
class WordDictionary { public: WordDictionary() { trie = new TrieNode(); } void addWord(string word) { insert(trie,word); } bool search(string word) { return dfs(word, 0, trie); }
bool dfs(const string & word,int index,TrieNode * node) { if (index == word.size()) { return node->isEnd; } char ch = word[index]; if (ch >= 'a' && ch <= 'z') { TrieNode * child = node->child[ch - 'a']; if (child != nullptr && dfs(word, index + 1, child)) { return true; } } else if (ch == '.') { for (int i = 0; i < 26; i++) { TrieNode * child = node->child[i]; if (child != nullptr && dfs(word, index + 1, child)) { return true; } } } return false; } private: TrieNode * trie; };
|
复杂度分析
时间复杂度:初始化为 O(1),添加单词为 O(∣S∣),搜索单词为 O(∣Σ∣∣S∣),其中 ∣S∣ 是每次添加或搜索的单词的长度,Σ 是字符集,这道题中的字符集为全部小写英语字母,∣Σ∣=26。最坏情况下,待搜索的单词中的每个字符都是点号,则每个字符都有 ∣Σ∣ 种可能。
空间复杂度:O(∣T∣⋅∣Σ∣),其中 ∣T∣ 是所有添加的单词的长度之和,Σ是字符集,这道题中的字符集为全部小写英语字母,∣Σ∣=26 。