139. Word Break [Medium]

自己不会做

Discuss里面BFS、DFS动态规划汇总好帖:

https://leetcode.com/problems/word-break/discuss/597051/Java-DP-and-BFS-Solutions-Clean-Code​​​​​​​

本题题意有点难懂:

判断目标字符串s,能否被分解为输入的字符串list中的一个或多个字符串

更好理解的说法是:

字符串list中的字符串,能否拼成目标字符串s(list中的字符串可以重复使用)

最终解法:动态规划DFS,或BFS

无论用DFS或BFS,都要注意搜索记忆化,否则会提交超时

通过下面多段代码运行结果对比,可见数组比Set用作记忆消耗的空间少很多

即使数组的长度更长(要s.length()甚至s.length()+1),Set只需要记录被访问过的几个String或数组下标

动态规划

自顶而下(recursion + mem)

/**
 * recursion + mem,相对容易想到的方法
 * 必须记忆化搜索(用mem),否则会提交超时,因为可能会重复搜索已判断过不能分解成dict里面的字符串的子串
 * Runtime: 6 ms, faster than 64.24% 
 * Memory Usage: 38.8 MB, less than 90.06%
 */
class Solution {
    public boolean wordBreak(String s, List wordDict) {
        return backTrack(s, wordDict, new ArrayList());
    }

    private boolean backTrack (String s, List wordDict, List mem) {
        if (s.equals("")) { // string has been separated into strings in wordDict successfully
            return true;
        }
        if (mem.contains(s)) { // s cannot be separated as strings in wordDict
            return false;
        }
        for (int i = 1; i <= s.length() && !res; i++) { // add "!res" to the condition, as when res become true further recursion is unnecessary
            if (wordDict.contains(s.substring(0, i)) && backTrack(s.substring(i), wordDict, mem)) { // found a substring starting with s's head in wordDict, truncate the substring from wordDict, and do recursion to see if the remained part could be separated to strings in wordDict too
                return true;
            }
        }
        mem.add(s); // s cannot be separated as strings in wordDict, remember it by adding s to mem
        return false;
    }
}
/**
 * recursion + mem,把mem从上一段的list类型改成array类型
 * Runtime: 5 ms, faster than 76.93%
 * Memory Usage: 38.7 MB, less than 90.06%
 */
class Solution {
    public boolean wordBreak(String s, List wordDict) {
        return backTrack(s, 0, wordDict, new Boolean[s.length()]);
    }
    
    private boolean backTrack(String s, int start, List wordDict, Boolean[] mem) {
        if (start == s.length()) {
            return true;
        }
        if (mem[start] != null) {
            return mem[start];
        }
        for (int end = start + 1; end <= s.length(); end++) {
            if (wordDict.contains(s.substring(start, end)) && backTrack(s, end, wordDict, mem)) {
                mem[start] = true;
                return true;
            }
        }
        mem[start] = false;
        return false;
    }
}

左上到右下(最常规的动态规划思路方向)

/**
 * 动态规划,左上到右下的动态规划思想
 * Runtime: 6 ms, faster than 64.24%
 * Memory Usage: 38.9 MB, less than 82.54%
 */
class Solution {
    public boolean wordBreak(String s, List wordDict) {
        Set set = new HashSet(wordDict); // remove duplicate items in wordDict, to minimize the number of string to compare
        int len = s.length();
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        
        for (int end = 1; end <= len; end++) {
            for (int start = 0; start < end; start++) {
                if (dp[start] && set.contains(s.substring(start, end))) {
                    dp[end] = true;
                    break; // when dp[end] become true, no further search is needed in the inner loop
                }
            }
        }
        return dp[len];
    }
}

BFS​​​​​​​

/**
 * BFS
 * 最初以0作为起始点,循环直到s.length(),找到所有能从起始点到达的点,加入队列
 * 再将队列中的各个点作为起始点,同样找到所有能到达的点加入队列
 * 用visited数组记录当前起始点是否已被访问向外延展过,如果已经找过从它出发能到达的点,则没有必要再从它出发一次
 * 当能访问的点到达s.length(),return true
 * 当所有起始点被访问完,还没有return,说明s不能拆分成wordDict中的字符串,return false
 * Runtime: 8 ms, faster than 31.54% 
 * Memory Usage: 39 MB, less than 76.60%
 */
class Solution {
    public boolean wordBreak(String s, List wordDict) {
        LinkedList q = new LinkedList();
        boolean[] visited = new boolean[s.length()]; // mark the idxs where we have started from before
        q.offer(0);
        while (!q.isEmpty()) {
            int start = q.poll();
            if (!visited[start]) { // first time starting from "start"
                for (int end = start + 1; end <= s.length(); end++) {
                    if (wordDict.contains(s.substring(start, end))) {
                        if (end == s.length()) {
                            return true;
                        }
                        q.offer(end);
                    }
                }
            }
            visited[start] = true;
        }
        return false;
    }
}
/**
 * 和前一段的不同在于用set代替了array来判断start是否被访问过
 * Runtime: 8 ms, faster than 31.54%
 * Memory Usage: 39.8 MB, less than 18.60%
 */
class Solution {
    public boolean wordBreak(String s, List wordDict) {
        LinkedList q = new LinkedList();
        Set visited = new HashSet(); // mark the idxs where we have started from before
        q.offer(0);
        while (!q.isEmpty()) {
            int start = q.poll();
            if (!visited.contains(start)) { // first time starting from "start"
                for (int end = start + 1; end <= s.length(); end++) {
                    if (wordDict.contains(s.substring(start, end))) {
                        if (end == s.length()) {
                            return true;
                        }
                        q.offer(end);
                    }
                }
            }
            visited.add(start);
        }
        return false;
    }
}

你可能感兴趣的:(刷题,leetcode,动态规划)