Add and Search Word - Data structure design
问题:
Design a data structure that supports the following two operations:
void addWord(word) bool search(word)
search(word) can search a literal word or a regular expression string containing only letters a-z
or .
. A .
means it can represent any one letter.
For example:
addWord("bad") addWord("dad") addWord("mad") search("pad") -> false search("bad") -> true search(".ad") -> true search("b..") -> true
Note:
You may assume that all words are consist of lowercase letters a-z
.
hint:
You should be familiar with how a Trie works. If not, please work on this problem: Implement Trie (Prefix Tree) first.
解决:
① 要求实现添加和查找单词,实际上是实现向字典树种添加词组,并可以查找匹配词组。与 Implement Trie (Prefix Tree) 相似,这道题里面'.'可以代替任意字符,所以一旦有了'.',就需要查找所有的子树,只要有一个返回true,整个search函数就返回true。
class TrieNode{//222ms
char c;//当前节点的字符
Map<Character,TrieNode> children = new HashMap<>();//当前节点的子节点,以当前节点的字符作为key
boolean isLeaf;
public TrieNode(){}
public TrieNode(char c){
this.c = c;
}
}
public class WordDictionary{
private TrieNode root;//指定根节点,没有字符。
public WordDictionary(){
root = new TrieNode();//根节点没有字符
}
public void addWord(String word){
Map<Character,TrieNode> cur = root.children;//从根节点的子节点开始向下添加
for (int i = 0;i < word.length() ;i ++ ) {
char c = word.charAt(i);//当前要插入的字符
TrieNode tmp = null;//对应的节点
if (cur.containsKey(c)) {//当前字符对应的节点已经存在了
tmp = cur.get(c);//获取遍历到的节点
}else{
tmp = new TrieNode(c);
cur.put(c,tmp);
}
cur = tmp.children;
if (i == word.length() - 1) {
tmp.isLeaf = true;
}
}
}
public boolean search(String word){
return dfsSearch(root.children,word,0);
}
public boolean dfsSearch(Map<Character,TrieNode> cur,String word,int i){
if (i == word.length()) {
if (cur.size() == 0) {//节点与单词都遍历完了
return true;
}else{
return false;
}
}
char c = word.charAt(i);
if (cur.containsKey(c)) {
if (i == word.length() - 1 && cur.get(c).isLeaf) {
return true;
}
return dfsSearch(cur.get(c).children,word,i + 1);
}else if(c == '.'){
boolean res = false;
for (Map.Entry<Character,TrieNode> child : cur.entrySet()) {
if (i == word.length() - 1 && child.getValue().isLeaf) {
return true;
}
if(dfsSearch(child.getValue().children,word,i + 1)){
res = true;
}
}
return res;
}else{
return false;
}
}
}
② 使用数组代替Map。
class TrieNode{//186ms
TrieNode[] children;
boolean isLeaf;
public TrieNode(){//单词中全部为26个小写字母。
children = new TrieNode[26];
}
}
public class WordDictionary {
TrieNode root;
public WordDictionary(){
root = new TrieNode();//根节点为空
}
public void addWord(String word){
TrieNode cur = root;
for (int i = 0;i < word.length() ;i ++ ) {//构造前缀树
char c = word.charAt(i);
int index = c - 'a';//在孩子节点中的位置
if (cur.children[index] == null) {
TrieNode tmp = new TrieNode();
cur.children[index] = tmp;
cur = tmp;
}else{
cur = cur.children[index];
}
}
cur.isLeaf = true;
}
public boolean search(String word){
return dfsSearch(root,word,0);
}
public boolean dfsSearch(TrieNode cur,String word,int i){
if (i == word.length() && cur.isLeaf) {
return true;
}
if (i >= word.length()) {
return false;
}
char c = word.charAt(i);
if (c == '.') {
boolean res = false;
for (int j = 0;j < 26 ;j ++ ) {//查看是否有匹配的子节点
if (cur.children[j] != null) {
if (dfsSearch(cur.children[j],word,i + 1)) {
res = true;
break;
}
}
}
if (res) {
return true;
}
}else{
int index = c - 'a';
if (cur.children[index] != null) {
return dfsSearch(cur.children[index],word,i + 1);
}else{
return false;
}
}
return false;
}
}
③ 在discuss中看到的,使用map保存节点及其对应路径上的长度,值为字符串的长度,键为对应的字符串的长度。
public class WordDictionary{//160ms
//使用map构造前缀树,使用链表保存节点值.键为字符串长度,值为对应的字符串
Map<Integer,List<String>> map = new HashMap<>();
public void addWord(String word){
int index = word.length();
if (! map.containsKey(index)) {
List<String> list = new ArrayList<>();
list.add(word);
map.put(index,list);
}else{
map.get(index).add(word);
}
}
public boolean search(String word){
int index = word.length();
if(! map.containsKey(index)){
return false;
}
List<String> list = map.get(index);
for (String s : list) {
if (isSame(s,word)) {
return true;
}
}
return false;
}
public boolean isSame(String root,String search){
if (root.length() != search.length()) {
return false;
}
for (int i = 0;i < root.length() ;i ++ ) {
if (search.charAt(i) != '.' && search.charAt(i) != root.charAt(i)) {
return false;
}
}
return true;
}
}