查看原文
其他

573,动态规划解单词拆分

博哥 数据结构和算法 2022-05-01

It dose not do to dwell on dreams and forget to live. 

人不能因为依赖梦想而忘记生活。

问题描述



给定一个非空字符串s和一个包含非空单词的列表wordDict,判定s是否可以被空格拆分为一个或多个在字典中出现的单词。


说明:

  1. 拆分时可以重复使用字典中的单词。

  2. 你可以假设字典中没有重复的单词。


示例 1:

输入:

s = "leetcode",

wordDict = ["leet", "code"]


输出: true

解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。

示例 2:

输入

s = "applepenapple",

wordDict = ["apple", "pen"]


输出: true

解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。

     注意你可以重复使用字典中的单词。

示例 3:

输入

s = "catsandog", 

wordDict = ["cats", "dog", "sand", "and", "cat"]


输出: false


动态规划解决



这题是让把字符串s拆分成一些子串,并且判断这些子串是否都存在字典wordDict中。


我们定义dp[i]表示字符串的前i个字符经过拆分是否都存在于字典wordDict中。如果要求dp[i],我们需要往前截取k个字符,判断子串[i-k+1,i]是否存在于字典wordDict中,并且前面[0,i-k]子串拆分的子串也是否都存在于wordDict中,如下图所示

前面[0,i-k]子串拆分的子串是否都存在于wordDict中只需要判断dp[i-k]即可,而子串[i-k+1,i]是否存在于字典wordDict中需要查找,所以动态规划的递推公式很容易列出来

dp[i]=dp[i-k]&&dict.contains(s.substring(i-k, i));

这个k我们需要一个个枚举,我们来看下最终代码

1public boolean wordBreak(String s, List<String> dict) {
2    boolean[] dp = new boolean[s.length() + 1];
3    for (int i = 1; i <= s.length(); i++) {
4        //枚举k的值
5        for (int k = 0; k <= i; k++) {
6            //如果往前截取全部字符串,我们直接判断子串[0,i-1]
7            //是否存在于字典wordDict中即可
8            if (k == i) {
9                if (dict.contains(s.substring(0, i))) {
10                    dp[i] = true;
11                    continue;
12                }
13            }
14            //递推公式
15            dp[i] = dp[i - k] && dict.contains(s.substring(i - k, i));
16            //如果dp[i]为true,说明前i个字符串结果拆解可以让他的所有子串
17            //都存在于字典wordDict中,直接终止内层循环,不用再计算dp[i]了。
18            if (dp[i]) {
19                break;
20            }
21        }
22    }
23    return dp[s.length()];
24}

上面代码有一个判断,就是截取的是前面全部字符串的时候要单独判断,其实当截取全部的时候我们只需要判断这个字符串是否存在于字典wordDict中即可,可以让dp[0]为true,dp[0]表示的是空字符串。这样代码会简洁很多,我们来看下

1public boolean wordBreak(String s, List<String> dict) {
2    boolean[] dp = new boolean[s.length() + 1];
3    dp[0] = true;//边界条件
4    for (int i = 1; i <= s.length(); i++) {
5        for (int j = 0; j < i; j++) {
6            dp[i] = dp[j] && dict.contains(s.substring(j, i));
7            if (dp[i]) {
8                break;
9            }
10        }
11    }
12    return dp[s.length()];
13}

这个和第一种写法不太一样,这个每次截取的方式如下图所示。

总结



这题我们也可以把它看作是一个完全背包问题,背包就是字符串s,而所选择的商品就是字典中的字符串,因为字典中的字符串可以选择多次并且没有限制,所以我们可以认为他是一个完全背包问题,完全背包问题我们以后会讲。其实这题还可以使用dfs来解决,由于这题写完的时候已经比较晚了,就留在明天讲吧。


572,动态规划解分割回文串 III

570,动态规划解回文串分割 IV

553,动态规划解分割回文串 II

551,回溯算法解分割回文串


截止到目前我已经写了500多道算法题了,为了方便大家阅读,我把部分算法题整理成了pdf文档,目前有1000多页,大家可以在公众号中回复关键字“pdf”即可获取下载链接。


你点的每个赞,我都认真当成了喜欢

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存