一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。
前言
力扣第140题 单词拆分 II
如下所示:
给定一个字符串 s
和一个字符串字典 wordDict
,在字符串 s
中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。
注意: 词典中的同一个单词可能在分段中被重复使用多次。
示例 1:
输入: s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
输出: ["cats and dog","cat sand dog"]
一、思路
这一题与前面一题 力扣第139题-单词拆分 基本是一样的,都是用单词字典 wordDict
中的单词来拼凑出目标字符串。但是有一点不同:这一题需要知道由单词字典 wordDict
中单词拼凑出字符串 s
的所有组合。
虽然题目中要求返回的结果列表中是以空格
隔开的句子,我们在分析的过程过程中可以先不考虑返回的形式,重要的是记录单词拼凑组合的内容
我们可以在第一题的基础上来看这一题,假设我们已经通过动态规划知道了子串 s[0 ~ i]
是否可以由单词字典中的单词组成,我们可以目标字符串的后面开始找到单词字典中的每个单词,假设 s[j ~ len]
为单词字典 wordDict
中的单词。那么我么后续可以继续向前寻找是否有单词存在,直到单词组合可以覆盖整个字符串。
综上所述,大致的步骤如下所示:
- 使用动态规划,找出子字符串
s[0 ~ i]
是否可以由单词组合成 - 从字符串的后面向前面找匹配的单词,依次向前,直到可以完整覆盖所有的字符串
图解算法
此处以示例1中的 s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
作为例子来分析
- 动态规划初始化子串是否可以由单词组成
该部分的动态规划部分与上一题完全一致,我这里使用的与上一题完全相同的实现代码
初始后的结果如下所示:
- 从字符串后面向前找
我们很轻易就能找到 dog
可以匹配,且此时剩余的字符串 catsand
也可以由单词组合成,即 dp[6] == true
- 继续向前找
我们很轻易就能找到 sand
可以匹配,且此时剩余的字符串 cat
也可以由单词组合成,即 dp[2] == true
- 第一组正确结果
同理可以匹配 cat
,此时的组合将整个字符串覆盖了,故得到了第一组正确的结果。
其他的结果集获取的过程同上所示。
二、实现
实现代码
实现代码与思路中保持一致
List<String> ret = new ArrayList<>();
public List<String> 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;
}
}
}
// 如果字符串不能匹配的话,则直接返回
if (!dp[len])
return ret;
// 递归
Deque<String> stack = new LinkedList<>();
dfs(s, len, wordDictSet, dp, stack);
return ret;
}
public void dfs(String s, int end, Set<String> wordDictSet, boolean[] dp, Deque<String> path){
if (end == 0){
// 收集结果
ret.add(String.join(" ", path));
}
// 从后往前走
for (String word : wordDictSet){
if (s.substring(0, end).endsWith(word) && dp[end - word.length()]){
path.push(word);
dfs(s, end-word.length(), wordDictSet, dp, path);
path.pop();
}
}
}
测试代码
public static void main(String[] args) {
String s = "catsanddog";
List<String> wordDict = Arrays.asList("cat","cats","and","sand","dog");
new Number140().wordBreak(s, wordDict);
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~