• [LeetCode#211]Add and Search Word


    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;
        }
    }
  • 相关阅读:
    约数个数 和 约数之和
    二分模板
    新生赛补题
    codefores刷题心得3 思维+dp(特别好玩)
    二叉树的遍历及例题
    团队作业七——团队作业分配
    WarPlane——游戏设计文档
    团队作业(五)
    团队作业(四)
    团队项目方案分析
  • 原文地址:https://www.cnblogs.com/airwindow/p/4772475.html
Copyright © 2020-2023  润新知