码迷,mamicode.com
首页 > 其他好文 > 详细

[LeetCode#211]Add and Search Word - Data structure design

时间:2015-08-31 11:27:23      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:

Problem:

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

Analysis:

This problem is just so elegant!!!It need to use the skill we have accumulated for those days‘s training.
The problem asks us to design a class that support "insert" and "search" operations, apparently it perfectly match the functionality of a prefix tree.
But for the search operation, we need to support vigous search, ‘.‘ could represent any character from ‘a‘ to ‘z‘.
How could we solve this prblem?

Below is a wrong solution I have implemented. 
Wrong solution:
public boolean search(String word) {
        TreeNode node = root;
        Queue<TreeNode> queue = new LinkedList<TreeNode> ();
        queue.add(node);
        
        char[] char_array = word.toCharArray();
        while (!queue.isEmpty()) {
            TreeNode cur_node = queue.poll();
            if (cur_node.word != null && cur_node.word.equals(word))
                return true;
            int level = cur_node.level;
            if (char_array[level+1] == ‘.‘) {
                for (int i = 0; i < 26; i++) {
                    if (cur_node.children[i] != null)
                        queue.offer(cur_node.children[i]);
                }
            }
            if (cur_node.children[char_array[level+1]-‘a‘] != null)
                queue.offer(cur_node.children[char_array[level+1]-‘a‘]);
        }
        return false;
    }
    
Runtime Error Message:
Line 51: java.lang.ArrayIndexOutOfBoundsException: -51
Last executed input:
addWord("a"),search(".")


A problem for this design is to ignore the ‘.‘ could actually vigously matach any character. If we encounter ‘.‘ in the pattern, we should try to match any character appears on this index.
Case:
["abc"]
pattern: "a.c"
search("a.c") should return abc. 
Apparently, it‘s a challenging! Since when reach the second character of "a.c", we should go on the search process, even we still know the situation of level 2. (where ‘c‘ may not exist).

If we store all those words into prefix tree, to search a word in the prefix tree is actually equal to find a word, starting from "" to the target word. We could use BFS or DFS for this search process. Once we encounter "." at level n, we should search all existing children branches at that TreeNode. 

DFS is a way, we need to write a recursive call for this, which I really don‘t want for such tiny data strucutre. I decide to use BFS method. Challenge in using BFS method:
We need to match one character by one charcter, which means we must know the poped TreeNode‘s level at the prefix tree. One way is to use the ugly "cur_num, next_num, level", one is the use the powerful skill we have learned in word ladder, add the level information into the TreeNode.
class TreeNode {
    String word;
    int level;
    TreeNode[] children;
    public TreeNode(int level) {
        this.word = null;
        this.level = level;
        this.children = new TreeNode[26];
    }
}
Once we poped a node outside the queue, we could know which character in the pattern it should compare against!
Note: we actually use "if (childer[i] != null)" to check if a character appears!!!
------------------------------------------------------------------------------------
We start the search process from level "-1", which means empty string. 
Once we pop a tree node from the queue, it means for a given pattern, we have already matched all characters appears before(including) cur_node.level. 
pattern: [abcdef]
....
cur_node = queue.poll();
cur_node.level = 5
it means the path has already matched [abcdef]. 
What we need to do at this node is to decide if we can also find a path match cur_node[6]. 

Iff cur_node[6] == ‘.‘, we could use all available children of cur_node for this match.

if (char_array[level+1] == ‘.‘) {
    for (int i = 0; i < 26; i++) {
        if (cur_node.children[i] != null)
            queue.offer(cur_node.children[i]);
        }
    }
    
iff cur_node[6] == ‘a‘ to ‘z‘, we must test if the childern[cur_node[6]-‘a‘] exist.

else if (cur_node.children[char_array[level+1]-‘a‘] != null) {
    queue.offer(cur_node.children[char_array[level+1]-‘a‘]);
} 

To insert a word into the prefix is easy, we just need add extra level information for next character.
 public class WordDictionary {
    TreeNode root = new TreeNode(-1);
    ...
 }
 
 public void addWord(String word) {
        TreeNode node = root;
        for (char c : word.toCharArray()) {
            if (node.children[c-‘a‘] == null)
                node.children[c-‘a‘] = new TreeNode(node.level+1);
            node = node.children[c-‘a‘];
        }
        node.word = word;
 }
------------------------------------------------------------------------------

Mistakes:

During the process, I have made some mistakes for the implementation.
Lesson: Once you are sure with the "insert" process, you should never doubt about it, you should focus on the things that you are not sure with. 

A mistake.
1. Return abruptly before checking all enqueued TreeNodes. 

Code snippt:
        while (!queue.isEmpty()) {
            TreeNode cur_node = queue.poll();
            if (cur_node.word != null && compareString(cur_node.word, word))
                return true;
            int level = cur_node.level;
            if (level + 1 > word.length()-1)
                return false;
            
            if (char_array[level+1] == ‘.‘) {
                for (int i = 0; i < 26; i++) {
                    if (cur_node.children[i] != null)
                        queue.offer(cur_node.children[i]);
                }
            } else if (cur_node.children[char_array[level+1]-‘a‘] != null) {
                queue.offer(cur_node.children[char_array[level+1]-‘a‘]);
            } else {
                return false;
            }
        }



mistake 1.1 the current path‘s word exceed the "pattern‘s" length, then return false:
wrong case:
wordDictionary.addWord("adds");
wordDictionary.addWord("addee");
wordDictionary.search("add.");
Cause ‘e‘ appears before ‘s‘, then TreeNode(‘e‘) enqueued the queue before TreeNode(‘s‘). 
When the TreeNode(‘e‘) was poped out, the TreeNode(‘e‘).word == null, and the children ‘e‘ would exceed the pattern‘s length. Apparently this path is not right!!!
But, the exsiting TreeNode(‘s‘) is right!!! we should not return false before checking it. "continue" is always a good choice for skipping search along this drection!
In the BFS problem, you should not use "return false" blindly in the middle of queue, it should be used after the queue.

while (!queue.isEmpty()) {
    ....
    if (level + 1 > word.length()-1)
        continue;
    ....
        }
return false;

mistake 1.2 the same mistake also was made for when a path is not right.
    if (cur_node.children[i] != null) {
        queue.offer(cur_node.children[i]);
    } else if (cur_node.children[char_array[level+1]-‘a‘] != null) {
        queue.offer(cur_node.children[char_array[level+1]-‘a‘]);
    } else {
        return false;
    }
Ugly and wrong!!!

Warnning: Always be cautious to use return fasle during BFS search. (in a queue)

Solution:

class TreeNode {
    String word;
    int level;
    TreeNode[] children;
    public TreeNode(int level) {
        this.word = null;
        this.level = level;
        this.children = new TreeNode[26];
    }
}


public class WordDictionary {
    
    TreeNode root = new TreeNode(-1);

    // Adds a word into the data structure.
    public void addWord(String word) {
        TreeNode node = root;
        for (char c : word.toCharArray()) {
            if (node.children[c-‘a‘] == null)
                node.children[c-‘a‘] = new TreeNode(node.level+1);
            node = node.children[c-‘a‘];
        }
        node.word = word;
    }

    // Returns if the word is in the data structure. A word could
    // contain the dot character ‘.‘ to represent any one letter.
    public boolean search(String word) {
        TreeNode node = root;
        Queue<TreeNode> queue = new LinkedList<TreeNode> ();
        queue.add(node);
        
        char[] char_array = word.toCharArray();
        while (!queue.isEmpty()) {
            TreeNode cur_node = queue.poll();
            int level = cur_node.level;
            if (cur_node.word != null && level == word.length()-1) {
                if (compareString(cur_node.word, word))
                    return true;
            }
            if (level + 1 > word.length()-1)
                continue;
            if (char_array[level+1] == ‘.‘) {
                for (int i = 0; i < 26; i++) {
                    if (cur_node.children[i] != null)
                        queue.offer(cur_node.children[i]);
                }
            } else if (cur_node.children[char_array[level+1]-‘a‘] != null) {
                queue.offer(cur_node.children[char_array[level+1]-‘a‘]);
            }
        }
        return false;
    }
    
    
    private boolean compareString(String s, String match) {
        if (s.length() != match.length())
            return false;
        for (int i = 0; i < s.length(); i++) {
            if (match.charAt(i) != ‘.‘ && match.charAt(i) != s.charAt(i))
                return false;
        }
        return true;
    }
}

 

[LeetCode#211]Add and Search Word - Data structure design

标签:

原文地址:http://www.cnblogs.com/airwindow/p/4772475.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!