一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
前言
力扣第139题 单词拆分
如下所示:
给你一个字符串 s
和一个字符串列表 wordDict
作为字典。请你判断是否可以利用字典中出现的单词拼接出 s
。
注意: 不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
一、思路
题目的意思很简单,就是使用 wordDict 中的单词拼凑出目标字符串 s (单词可以重复使用)。我很容易就想到使用 递归
来逐个使用 wordDict
中的单词,直到能够完整拼凑目标字符串。递归的代码如下所示:
但是非常可惜的是,遇到一些较长的测试用例会出现超时间的问题。
动态规划
那如何优化递归呢,以减少寻找的次数。一般来说可行的思路就是使用 动态规划
来优化递归。那如果做呢?
- 定义
dp[]
数组
我们注意到在递归中每次都要重新选取 wordDict
中的单词,那么要怎么优化这个事情呢?
我们可以设 dp[i]
为: s[0 ~ i-1]
的字符串 s1
是否可以由 wordDict
中的单词组成。
- 状态转移方程
我们假设知道了 dp[0 ~ i-1]
的值,我们可以枚举 s[j ~ i]
是否在 dictWord
中,然后再根据 dp[j]
的值。只要在枚举的过程中出现了一次 true
就表示 s[0 ~ i-1]
可以由 dictWord
中的单词组成。通过上面的分析,很明显我们可以得出下面这个状态转移方程:
dp[i]=dp[j] && dictWord.contains(s[j ~ i−1]),(0 =< j <= i-1)
综上所述,使用递归的大致步骤如下所示:
- 使用
dp[i]
存储子字符串是否可以由dictWord
组成 - 从前往后遍历,枚举
o ~ i
的子串匹配情况,就可以得到dp[i+1]
的值了
二、实现
实现代码
实现代码与思路中保持一致,如下所示:
public boolean wordBreak(String s, List<String> wordDict){
int len = s.length();
Set<String> wordDictSet = new HashSet(wordDict);
boolean[] dp = new boolean[len+1];
dp[0] = true; // 空字符串
for (int i=1; i <= len; i++) {
for (int j=0; j < i; j++) {
if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
dp[i] = true; // 有 true 就立刻去初始化下一个dp
break;
}
}
}
return dp[s.length()];
}
测试代码
public boolean wordBreak(String s, List<String> wordDict){
int len = s.length();
Set<String> wordDictSet = new HashSet(wordDict);
boolean[] dp = new boolean[len+1];
dp[0] = true; // 空字符串
for (int i=1; i <= len; i++) {
for (int j=0; j < i; j++) {
if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
dp[i] = true; // 有 true 就立刻去初始化下一个dp
break;
}
}
}
return dp[s.length()];
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~