使用动态规划拆解单词

711 阅读2分钟

问题描述

给定一个非空字符串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个字符经过拆分都是存在于字典中的单词。如果要求dp[i],需要往前截取k个字符,判断子串[i-k+1,i]是否存在于字典wordDict中,并且前面[0,i-k] 子串拆分的子串也是否都存在于WordDict中,如下图

image.png

  前面的[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需要一个个的进行枚举,最终代码如下

public boolean wordBreak(String s,List<String> dict){
    boolean[] dp =new boolean[s.length()+1]
    for(int i = 1;i<=s.length();i++){
        //枚举k的值
        for(int k = 0;k<=i;k++){
            //如果往前截取全部的字符串,直接判断子串[0,i-1]
            // 是否存在于字典wordDict中即可
            if(k==i){
                if(dict.contains(s.substring(0,i))){
                    dp[i] = true;
                    continue;
                }
            }
            //递推公式
            dp[i] = dp[i-k]&&dict.contains(s.substring(i-k,i));
            // 如果dp[i] 为true,说明前i个字符串结果拆解可以让他的所有子串
            // 都存在于字典wordDict中,直接终止内层循环,不用再计算dp[i]了。
            if(dp[i]){
                break;
            }
        }
    }
    return dp[s.lenght()];
    
}

  上面代码有一个判断,就是截取的是前面全部字符串的时候需要单独判断,其实当截取全部的时候只需要判断这个字符串是否存在于字典中,可以让dp[0] 为true,dp[0] 表示的是空字符串,代码如下

public boolean wordBreak(String s,List<String> dict){
    boolean[] dp =new boolean[s.length()+1]
    dp[0] = true;
    for(int i = 1;i<=s.length();i++){
        //枚举k的值
        for(int k = 0;k<=i;k++){
            dp[i] = dp[i-k]&&dict.contains(s.substring(i-k,i));
            if(dp[i]){
                break;
            }
        }
    }
    return dp[s.lenght()];
}

逻辑如下图所示

image.png

总结

  这个问题可以看做一个完全背包问题,背包就是字符串s,而所选择的商品就是字典中的字符串,因为字典中的字符串可以选择多次并且没有限制,所以可以认为他是一个完全背包问题,完全背包问题在后续的分享中会有,这个问题还可以用dfs来解决。