leetcode425

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, where 0 ≤ k < max(numRows, numColumns).
For example, the word sequence ["ball","area","lead","lady"] forms a word square because each word reads the same both horizontally and vertically.
b a l l
a r e a
l e a d
l a d y
Note:
1. There are at least 1 and at most 1000 words.
2. All words will have the exact same length.
3. Word length is at least 1 and at most 5.
4. Each word contains only lowercase English alphabet a-z.

DFS。with prune。
最基础的DFS思想就是,在某一中间状态,前面几行已经被一些单词填了,我只管接下来这行填什么。我可以试着把单词本里的单词都一个个试着放在这一行,接着dfs,再回溯。
但这种蛮力的dfs时间复杂度太高了,需要做一些提前prune。本题prune的基本思想是,行要依附于与列相等的规则。当我们最面几列的prefix是什么样已经定下来的时候,我们就可以强制要求后面几行的prefix是什么样,从而在以某些prefix开头的单词本dict根本不存在的时候,提前终结可能性。

prune1: 填第l行的时候,要和第l列读着一样,而第l列已经有了前面一些prefix了,读出这个prefix,接下来可以插入第l行的候选单词也要以这个prefix开头。
prune2: 填第l行第时候,不是所有上面以prefix开头的就可以了,还要进一步检查一下会不会坑队友。这一行填进去有0~l行了,那么l+1~n-1行的prefix也可以确定了,你确认一下是不是后面每行的prefix都还是有希望的,词典里有的。要是有任何一行被弄的没希望了,那你就算这行暂时加进去没问题,之后迟早一天注定会死,也还是就别加了。这个解法可以提前少掉后面几行(还没到注定要死行前)的好几种不同组合可能饿了。

细节:
1.额外写一个收集所有以某一prefix开头的单词的dict。类型<String, Set<String>>。注意所有原始单词都是以""前缀开头的。
2.这题一些固定的一直要递归用到的变量很多,都放在参数列表里有点冗余,其实可以提出来做全局变量。dict, n

实现:

class Solution {
    public List<List<String>> wordSquares(String[] words) {
        List<List<String>> ans = new ArrayList<>();
        if (words == null || words.length == 0) {
            return ans;
        }
        
        int n = words[0].length();
        Map<String, Set<String>> dict = prefixDict(words);
        dfs(new ArrayList<String>(), ans, 0, n, dict);
        return ans;
    }
    
    private void dfs(List<String> crt, List<List<String>> ans, int l, int n, Map<String, Set<String>> dict) {
        if (l == n) {
            ans.add(new ArrayList<String>(crt));
            return;
        }
        
        String prefix = "";
        for (int i = 0; i < l; i++) {
            prefix += crt.get(i).charAt(l);
        }
        
        if (!dict.containsKey(prefix)) {
            return;
        }
        
        Set<String> set = dict.get(prefix);
        for (String s : set) {
            if (!canTry(l, s, crt, dict, n)) {
                continue;
            }
            crt.add(s);
            dfs(crt, ans, l + 1, n, dict);
            crt.remove(crt.size() - 1);
        }
        
    }
    
    private Map<String, Set<String>> prefixDict(String[] words) {
        Map<String, Set<String>> map = new HashMap<>();
        map.put("", new HashSet<String>());
        for (String s : words) {
            String pref = "";
            map.get(pref).add(s);
            for (int i = 0; i < s.length(); i++) {
                pref += s.charAt(i);
                map.putIfAbsent(pref, new HashSet<String>());
                map.get(pref).add(s);
            }
        }
        return map;
    }
    
    private boolean canTry(int l, String nextWord, List<String> crt, Map<String, Set<String>> dict, int n) {
        for (int j = l + 1; j < n; j++) {
            String prefix = "";
            for (int i = 0; i < l; i++) {
                prefix += crt.get(i).charAt(j);
            }
            prefix += nextWord.charAt(j);
            if (!dict.containsKey(prefix)) {
                return false;
            }
        }
        return true;
    }
}
原文地址:https://www.cnblogs.com/jasminemzy/p/9644282.html