leetcode139. 单词拆分

1.题目描述:

给你一个字符串s和一个字符串列表wordDict作为字典。请你判断是否可以利用字典中出现的单词拼接出s。注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

leetcode139. 单词拆分_第1张图片

2.动态规划:

这道题实际是代码随想录背包问题模块的最后一题,首先是完全背包,其次与先前的求组合及求排列的背包问题都有所不同,他是求一种特定的排列,因此只能先遍历背包再遍历物品,否则虽然先遍历的物品可以当次重复遍历,但无法在下次遍历出现,也就是示例2输出false个人感觉代码随想录这里出错了,也有可能是我写不出正确的先遍历物品再遍历背包的代码。以下代码就是示例13正确,示例2错误。状态转移方程就是基于循环增长的字串是否在集合中出现,代码中见。

class Solution {
    public boolean wordBreak(String s, List wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int i = 0; i < wordDict.size(); i++) {//先遍历物品
            for (int j = wordDict.get(i).length(); j <= s.length(); j++) {//再遍历背包
                if (dp[j - wordDict.get(i).length()] && 
                wordDict.contains(s.substring(j - wordDict.get(i).length(), j))) dp[j] = true;
            }
        }
        return dp[s.length()];
    }
}

先遍历背包再遍历物品(这里的物品也非严格意义上的物品,而是对字符串的划分):

class Solution {
    public boolean wordBreak(String s, List wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int i = 0; i <= s.length(); i++) {//先遍历背包
            for (int j = 0; j < i; j++) {//再遍历物品
                if (wordDict.contains(s.substring(j,i)) && dp[j]) dp[i] = true;
            }
        }
        return dp[s.length()];
    }
}

3.记忆化回溯:

class Solution {
    public boolean wordBreak(String s, List wordDict) {
        Set wordDictSet = new HashSet(wordDict);
        int[] memory = new int[s.length()];
        return backTrack(s, wordDictSet, 0, memory);
    }
    
    public boolean backTrack(String s, Set wordDictSet, int startIndex, int[] memory) {
        if (startIndex >= s.length()) return true;
        if (memory[startIndex] != 0) return memory[startIndex] == 1 ? true : false;//1表示可以拼出i及以后的字符子串,-1则不能
        for (int i = startIndex; i < s.length(); i++) {
            String word = s.substring(startIndex, i + 1);
            if (wordDictSet.contains(word) && backTrack(s, wordDictSet, i + 1, memory)) {
                memory[startIndex] = 1;
                return true;
            }
        }
        memory[startIndex] = -1;//走到这里则不能拼出startIndex及以后字符子串
        return false;
    }
}

二刷回溯:与leetcode140. 单词拆分 II类似,但是超时。

class Solution {  
    public boolean wordBreak(String s, List wordDict) {
        return backTracking(s, wordDict, 0);
    }

    public boolean backTracking(String s, List wordDict, int index) {
        if (index == s.length()) return true;
        for (int i = index; i < s.length(); i++) {
            String sub = s.substring(index, i + 1);
            if (!wordDict.contains(sub)) continue;//剪枝
            if (backTracking(s, wordDict, i + 1)) return true;
        }
        return false;
    }
}

 回溯加入记忆化:详细解释见leetcode题解。

class Solution {
    public boolean wordBreak(String s, List wordDict) {
        int[] flag = new int[s.length()];
        return backTracking(s, wordDict, 0, flag);
    }

    public boolean backTracking(String s, List wordDict, int index, int[] flag) {
        if (index == s.length()) return true;
        if (flag[index] == -1) return false;
        for (int i = index; i < s.length(); i++) {
            String sub = s.substring(index, i + 1);
            if (!wordDict.contains(sub)) continue;//剪枝
            if (backTracking(s, wordDict, i + 1, flag)) return true;
        }
        flag[index] = -1;//走到这里说明当前index后无法拆成功,回溯时再遇到直接返回false
        return false;
    }
}

你可能感兴趣的:(动态规划,回溯算法,leetcode,数据结构,java,算法,动态规划)