zoukankan      html  css  js  c++  java
  • [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;
        }
    }
  • 相关阅读:
    周4早上搜索引擎分析 crmim.com| MSCRM开发者之家
    Bat命令学习
    sqlserver日期函数
    ubunto应用软件
    sql for xml
    win7x64 连接oracle 客户端 vs 2010调试 提示“ORA12154: TNS: 无法解析指定的连接标识符 ”ORA06413 问题(转)
    CentOS Rsync服务端与Windows cwRsync客户端实现数据同步
    怎么引导2岁孩子洗手问题
    Libnfcinstallation
    Asterisk资料
  • 原文地址:https://www.cnblogs.com/airwindow/p/4772475.html
Copyright © 2011-2022 走看看