140. Word Break II
题目描述
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.
Note:
The same word in the dictionary may be reused multiple times
in the segmentation.
You may assume the dictionary does not contain duplicate
words.
Example 1:
Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
"cats and dog",
"cat sand dog"
]
Example 2:
Input:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
"pine apple pen apple",
"pineapple pen apple",
"pine applepen apple"
]
Explanation: Note that you are allowed to reuse a dictionary
word.
Example 3:
Input:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]
方法1
思路
递归版本:为了防止重复计算,使用了一个映射保存中间的对子串的递归函数调用结果(这是一种比较原始的利用动态规划的思想解决问题想法:利用映射保存中间结果,随时得到已有的结果)
LeetCode耗时20ms,内存消耗12MB
代码实现
class Solution {
public:
vector<string> wordBreak(string s, vector<string> wordDict) {
unordered_set<string> dict(wordDict.begin(),wordDict.end());
int n=s.length();
int minLen = n;
int maxLen = 0;
for(auto word:dict)
{
int temp = word.length();
minLen = min(minLen,temp);
maxLen = max(maxLen,temp);
}
unordered_map<string,vector<string>> m;
return wordBreakCore(dict,minLen,maxLen,s,m);
}
//注意,这里的递归函数设计为返回值为结果
vector<string> wordBreakCore(unordered_set<string> &dict,int minLen,int maxLen,string s,unordered_map<string,vector<string>> &m)
{
if(m.find(s)!=m.end())
return m[s];
vector<string> ret;
// 不进行分割
if(dict.find(s)!=dict.end())
ret.push_back(s);
// 枚举进行分割的情况
for(int l = minLen; l<s.length() && l<=maxLen; l++)
{
string left = s.substr(0,l);
if(dict.find(left)!=dict.end())
{
vector<string> right = wordBreakCore(dict,minLen,maxLen,s.substr(l),m);
// 可以成功分割
if(right.size() > 0)
{
for(auto str : right){
ret.push_back(left+" "+str);
}
}
}
}
m[s] = ret;
return ret;
}
};
方法2
思路
深度优先遍历,思路:DFS+利用动态规划得到的信息剪枝(能否划分),剪枝就是避免没有意义的递归的调用。
leetcode耗时12ms,内存消耗9.8MB
代码实现
class Solution {
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> dict(wordDict.begin(),wordDict.end());
int n=s.length();
vector<bool> dp(n+1,false);
dp[n] = true;//要合理的初始化使之满足递推公式的要求
int minLen = n;
int maxLen = 0;
for(auto word:dict)
{
int temp = word.length();
minLen = min(minLen,temp);
maxLen = max(maxLen,temp);
}
//这里的dp是从右边到左边递推保存信息,是为了下面
//调用递归函数方式的需要
//i+minLen==n,0+n==n
for(int i=n-minLen;i>=0;i--)
{
for(int l=minLen;l<=maxLen && i+l<=n;l++)
{
if(dict.find(s.substr(i,l))!=dict.end() && dp[i+l])
{
dp[i] = true;
break;
}
}
}
vector<string> ret;
if(dp[0] == false)
return ret;
dfs(dict,minLen,maxLen,dp,s,0,"",ret);
return ret;
}
//深度优先遍历,这里函数的返回类型为void,函数的接口参数cur
//设计为携带遍历过程中,从左到右的到这个位置为止的
//前半部分信息(也就是dfs所经过的路径分割元素拼接),如果函数
//接口设计为有返回值,则返回值为整个结果
void dfs(unordered_set<string> &dict, int minLen, int maxLen,vector<bool> &dp, string &s, int index,string cur,vector<string> &ret)
{
for(int l = minLen; l<=maxLen && l+index<=s.size();l++)
{
if(dict.find(s.substr(index,l))!=dict.end() && dp[index+l])
{ //这里只是为了拼接的需要,人为的将常见的dfs
//的递归终止情况提前到这里
if(index+l == s.size())//最后一个单词的情况
ret.push_back(cur+s.substr(index,l));
else//注意这里的拼接技巧,还不到最后一个单词的话,每一个单词后面加" ",遇到最后一个单词,只拼接单词,不加" "
dfs(dict,minLen,maxLen,dp,s, index+l,cur+s.substr(index,l)+" ",ret);
}
}
}
};
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)