资讯专栏INFORMATION COLUMN

Word Squares

JerryZou / 1034人阅读

摘要:题目链接暴力遍历,一个一个检查看符不符合要求。首先这种需要求出所有结果的题,一般都是用的。因为题目已经说了的长度范围是到,最多考虑五个单词即可。首先是肯定都需要的,两种或者。如果题目要求返回所有以特定的开头的单词,那么可以用。

Valid Word Square

题目链接:https://leetcode.com/problems...

暴力遍历,一个一个检查看符不符合要求。

    public boolean validWordSquare(List words) {
        /* words[i][j] == words[j][i]
         * j >= len(words) or i >= len(words[j]) return false
         */
         for(int i = 0; i < words.size(); i++) {
             String word = words.get(i);
             for(int j = 0; j < word.length(); j++) {
                 if(j >= words.size() || i >= words.get(j).length()) return false;
                 if(word.charAt(j) != words.get(j).charAt(i)) return false;
             }
         }
         
         return true;
    }
Word Squares

题目链接:https://leetcode.com/problems...

这道题如果用上一题的方法,一个一个试的话,时间复杂度太高。所以要另想办法。
首先这种需要求出所有结果的题,一般都是用dfs(backtracking)的。然后这道题的思路是单词一个一个确定。因为题目已经说了word的长度范围是1到5,最多考虑五个单词即可。

第一个单词:""为前缀,在words数组里随便取一个word1,确定长度

第二个单词:在剩下的words里取出一个以word1[1]为前缀的word2

第三个单词:在剩下的里取出一个以word1[2]+word2[2]为前缀的word3

第四个单词:在剩下的里取出一个以word1[3]+word2[3]+word3[3]为前缀的word4

第五个单词:在剩下的里取出一个以word1[4]+word2[4]+word3[4]+word4[4]为前缀的word5

所以这题需要快速的找到前缀,那么可以想到用hashmap或者trie tree。题目说了单词没有duplication,省去了查重的过程。

遍历words,把其中的一个单词当作1st word

找到第二个单词,加到square里面,接着找第三个单词......这是个backtracking的过程,如果prefix不在trie tree里面直接return

找第二个,第三个,。。。单词的过程用的是bfs,已经知道prefix之后,根据prefix找到trie tree里面对应的node,从改node开始bfs走剩下的长度,找到所有可能的node,检查这些node是否是单词的末尾,是的话就放入list里面,给上面dfs的method来用

public class Solution {
    public List> wordSquares(String[] words) {
        // hashmap or trie tree
        TrieNode root = buildTrieTree(words);
        
        // traverse all word int words, for the 1st word
        for(String word : words) {
            // len(word) == 1, itself is square
            if(word.length() == 1) {
                List cur = new ArrayList();
                cur.add(word);
                result.add(cur);
                continue;
            }
            List square = new ArrayList();
            square.add(word);
            dfs(root, square);
        }
        
        return result;
    }
    
    List> result = new ArrayList();
    /* from the idx position in words
     * 1st word: a = words[i]
     * 2nd word: b prefix = a.charAt(1)
     * 3rd word: c prefix = a.charAt(2) + b.charAt(2)
     * 4th word: d prefix = a.charAt(3) + b.charAt(3) + c.charAt(3)
     * 5th word: e prefix = a.charAt(4) + b.charAt(4) + c.charAt(4) + d.charAt(4)
     */
    private void dfs(TrieNode root, List square) {
        int len = square.get(0).length();
        int prefix_length = square.size();
        if(len == prefix_length) {
            result.add(new ArrayList(square));
            return;
        }
        String prefix = "";
        // find supposed prefix
        for(int i = 0; i < prefix_length; i++) prefix += square.get(i).charAt(prefix_length);
        // find word with that prefix in trie tree
        TrieNode node = root;
        for(int i = 0; i < prefix.length(); i++) {
            if(node.children[getIndex(prefix.charAt(i))] == null) return;
            node = node.children[getIndex(prefix.charAt(i))];
        }
        List next = bfs(node, len - prefix_length);
        if(next.size() == 0) return;
        for(String s : next) {
            square.add(s);
            dfs(root, square);
            square.remove(square.size() - 1);
        }
    }
    // find all possible words with certain prefix
    private List bfs(TrieNode node, int left) {
        List possible = new ArrayList();
        Queue q = new LinkedList();
        q.offer(node);
        while(left-- > 0) {
            if(q.isEmpty()) break;
            for(int j = q.size(); j > 0; j--) {
                TrieNode cur = q.poll();
                for(int i = 0; i < 26; i++) {
                    if(cur.children[i] != null) q.offer(cur.children[i]);
                }
            }
        }
        
        for(TrieNode cur : q) {
            if(cur.word != null) possible.add(cur.word);
        }
        return possible;
    }
    
    private int getIndex(char c) {
        return c - "a";
    }
    
    class TrieNode {
        TrieNode[] children = new TrieNode[26];
        String word = "";
    }
    
    private TrieNode buildTrieTree(String[] words) {
        TrieNode root = new TrieNode();
        for(String word : words) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                if(cur.children[getIndex(word.charAt(i))] == null) {
                    cur.children[getIndex(word.charAt(i))] = new TrieNode();
                }
                cur = cur.children[getIndex(word.charAt(i))];
            }
            cur.word = word;
        }
        return root;
    }
}

看了discussion的方法,直接在构造trie的时候,在node里面加上以它为prefix所有可能的word,这样多加了一个List的空间,但是省去了上面bfs找单词的时间。

public class Solution {
    public List> wordSquares(String[] words) {
        // hashmap or trie tree
        TrieNode root = buildTrieTree(words);
        
        // traverse all word int words, for the 1st word
        for(String word : words) {
            // len(word) == 1, itself is square
            if(word.length() == 1) {
                List cur = new ArrayList();
                cur.add(word);
                result.add(cur);
                continue;
            }
            List square = new ArrayList();
            square.add(word);
            dfs(root, square);
        }
        
        return result;
    }
    
    List> result = new ArrayList();

    private void dfs(TrieNode root, List square) {
        int len = square.get(0).length();
        if(len == square.size()) {
            result.add(new ArrayList(square));
            return;
        }
        // find all words with prefix
        List next = findWords(root, square);
        for(String s : next) {
            square.add(s);
            dfs(root, square);
            square.remove(square.size() - 1);
        }
    }
    
    private List findWords(TrieNode node, List square) {
        String prefix = "";
        int prefix_length = square.size();
        // find supposed prefix
        for(int i = 0; i < prefix_length; i++) prefix += square.get(i).charAt(prefix_length);
        // find word with that prefix in trie tree
        for(int i = 0; i < prefix.length(); i++) {
            if(node.children[getIndex(prefix.charAt(i))] == null) return new ArrayList();
            node = node.children[getIndex(prefix.charAt(i))];
        }
        return node.wordsWithPrefix;
    }
    
    private int getIndex(char c) {
        return c - "a";
    }
    
    class TrieNode {
        TrieNode[] children = new TrieNode[26];
        List wordsWithPrefix = new ArrayList();
    }
    
    private TrieNode buildTrieTree(String[] words) {
        TrieNode root = new TrieNode();
        for(String word : words) {
            TrieNode cur = root;
            for(int i = 0; i < word.length(); i++) {
                if(cur.children[getIndex(word.charAt(i))] == null) {
                    cur.children[getIndex(word.charAt(i))] = new TrieNode();
                }
                cur = cur.children[getIndex(word.charAt(i))];
                cur.wordsWithPrefix.add(word);
            }
        }
        return root;
    }
}
Trie Tree的构造方法

cc150上面给了Trie和TrieNode的构造方法。感觉lc里面主要是高清TrieNode的fields,以及对应的build一个Trie的方法。lc上一共7道trie的题,出现过的TrieNode的fields不同的选择好像就3种。
首先children是肯定都需要的,两种:array(TrieNode[])或者HashMap(Map)。都可以用,但是好像lc上的题基本用的都是array,character的范围比较小用array还是比较省空间。

然后还用到的fields有:isWord(boolean:结尾,判断是否形成一个单词), word(String:结尾,形成的单词)以及startsWith(List:以走到当前的TrieNode形成的String为prefix,所有的word)
如果题目只要求判断单词在不在字典里,isWord就够了。
如果题目要求返回String,那么一般用word。
如果题目要求返回所有以特定的prefix开头的单词,那么可以用startsWith。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66559.html

相关文章

  • [LeetCode] 425. Word Squares

    Problem Given a set of words (without duplicates), find all word squares you can build from them. A sequence of words forms a valid word square if the kth row and column read the exact same string, wh...

    ranwu 评论0 收藏0
  • 425 Word Squares

    摘要:我们要满足都可以作为开头的部分。按照代码的逻辑,走一遍填写好每一步被更新的样子。开始结束同一层从左向右走的时候会不断增长,直到最后形成以个单词相应位置长度加一,更新重新进行下一次的搜索。 题目描述请见leetcode 425 w a l l a r e a l e a d l a d y 在给定的词典里,找到一些单词,组成一个square, 第i行和第i列一样。(0,0) 这个点找到满...

    luzhuqun 评论0 收藏0
  • python学习:python的正式介绍

    摘要:中的注释是以开头,并且一直延申到该文本结束为止。例如的长度为使用过大的索引会产生一个错误但是在切片中,越界索引会被自动处理中的字符串不能被修改,它们是的。其中最常用的列表,可以通过方括号括起逗号分隔的一组值得到。 在下面的例子中通过提示符(>>>与...)的出现与否来区分输入和输出:如果你想复现这些例子,当提示符出现后,你必须在提示符后键入例子中的每一个词;不以提示符开头的那些行是解释...

    suxier 评论0 收藏0
  • 《HelloGitHub》第 68 期

    摘要:在线尝试的进程管理工具。项目包含了代码实现运行过程动画以及相关论文为系统提供人脸识别解锁电脑的工具。在线阅读教科书计算机体系结构基础第三版。 .markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-b...

    番茄西红柿 评论0 收藏2637
  • ECMAScript 6不完全教程

    摘要:从到有两种新的方式来声明变量用于声明块级作用于变量用于声明常量,其值不能被改变。更多信息箭头函数处理多个返回值一些函数或方法能通过数组或对象返回多个值。在中,总是需要创建中间变量来访问返回值。内置了模块语法,但并没有得到引擎良好支持。 1. 尝试ES6 这里有三种简单地方式用于ES6编程: Web浏览器:使用Babel REPL,可以将ES6编译成ES5的平台,并且并不需要安装。 命...

    姘存按 评论0 收藏0

发表评论

0条评论

JerryZou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<