leetcode-单词拆分

126 阅读2分钟

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。

每次临近过年,都会更加感受到时间匆匆,白驹过隙。捉不住的东西太多,不过心中有目标,每天也在尽力按照计划去达成,时间也算没有浪费。

题目

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

示例 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

思路

看到题目,第一感觉是完全背包的一个变种。字典中的字符串就是物品,可以使用0件或者n件,求最后是否能把背包完全装满。
不过顺着前几天做动态规划题目的思路,也比较容易理解。定义布尔数组f[],f[n]代表s的前n个字符组成的子串s[0]...s[n-1]是否能用字典拼接。那么求解f[n+1]的时候,可以从0开始遍历,把s[0]...s[n]拆解成2部分s[0]...s[k]和s[k+1]...s[n],后半部分刚好是一个字典中的单词,那么如果前半部分f[k+1]为true,那么f[n+1]也是true。注意k可以从0到n遍历,但是只要找到1种分割让f[n+1]=true,就不用继续遍历了。
上面的解法比较好理解,同时还存在常数级别的优化,我们遍历k的时候,从后往前遍历,那么当第2部分的长度超过字典中最长单词的长度时,遍历也可以停止了。

Java版本代码

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        int len = s.length();
        boolean[] f = new boolean[len+1];
        f[0] = true;
        for (int i = 1; i <= len; i++) {
            for (int j = 0; j < i; j++) {
                if (f[j] && wordDict.contains(s.substring(j, i))) {
                    f[i] = true;
                    break;
                }
            }
        }
        return f[len];
    }
}