139. 单词拆分以及字符串递归经验-传入右子串-voiddfs与全局变量-booldfs-记忆递归booldfs写法
问题
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
分析
拆分字符串,判断拆分得到的子串是否在dict中。显然,dfs,需要回溯。超时,需要记忆。递推还没想怎么写,不如递归好写。
经验:字符串与递归结合,将右子串作为参数即可,不需要左右边界
代码
// 返回值递归,比全局变量递归剪枝,35/47,加了记忆后如下,可以通过
class Solution {
public:
int s_n = 0;
vector<string> d;
unordered_set<string> us;
unordered_map<string, bool> mem;
// 经验:字符串与递归结合,将右子串作为参数即可,不需要左右边界
bool dfs(string left_s) {
if (mem.find(left_s) != mem.end()) {return mem[left_s];}
if (us.find(left_s) != us.end()) {mem.insert(make_pair(left_s, true)); return true;}
int left_s_n = left_s.size();
for (int i = 1; i <= left_s_n; i++) {
if (us.find(left_s.substr(0, i)) != us.end()) {
if (dfs(left_s.substr(i))) {
mem.insert(make_pair(left_s, true));
return true;
};
mem.insert(make_pair(left_s, false));
}
}
return false;
}
bool wordBreak(string s, vector<string>& wordDict) {
this->d = wordDict;
s_n = s.size();
for (int i = 0; i < wordDict.size(); i++) {
us.insert(wordDict[i]);
}
bool res = dfs(s);
return res;
}
};
补一个全局变量递归。
// 法一、全局变量递归,最慢,35 / 47 个通过的测试用例
class Solution {
public:
bool res = false;
int s_n = 0;
vector<string> d;
unordered_set<string> us;
void dfs(string left_s) {
if (us.find(left_s) != us.end()) {res = true; return;}
int left_s_n = left_s.size();
for (int i = 1; i <= left_s_n; i++) {
if (us.find(left_s.substr(0, i)) != us.end()) {
dfs(left_s.substr(i));
}
}
}
bool wordBreak(string s, vector<string>& wordDict) {
this->d = wordDict;
s_n = s.size();
for (int i = 0; i < wordDict.size(); i++) {
us.insert(wordDict[i]);
}
dfs(s);
return res;
}
};
new
如果不传入右字串,传入要处理的起始位置,string设为全局变量,也是可以的。
从下面的例子来看void dfs()配合全局变量,到bool dfs(),再到记忆递归的bool dfs()的写法。
// void dfs()配合全局变量
class Solution {
private:
int x = 0;
unordered_map<string, int> um;
int n = 0;
string s;
bool res = false;
void dfs(int x) {
if (x >= n) {res = true; return ;}
for (int i = x; i < n; i++) {
string t = s.substr(x, i-x+1);
if (um.count(t)) {
dfs(i+1);
}
}
}
public:
bool wordBreak(string s, vector<string>& wordDict) {
n = s.size();
this->s = s;
for (const string& a : wordDict) {
um[a]++;
}
dfs(0);
return res;
}
};
修改返回值,同时dfs()改为if(dfs()) {return true;}
class Solution {
private:
int x = 0;
unordered_map<string, int> um;
unordered_map<int, int> mem;
int n = 0;
string s;
bool res = false;
bool dfs(int x) {
if (x >= n) {return true;} // 1 能走完全部s,说明可以分割,返回true
for (int i = x; i < n; i++) {
string t = s.substr(x, i-x+1);
if (um.count(t)) {
if (dfs(i+1)) {return true;} // 2 如果上层返回true了,这里应该直接返回true
}
}
return false; // 3 不行了,上层也没有返回true,那就返回false
}
public:
bool wordBreak(string s, vector<string>& wordDict) {
n = s.size();
this->s = s;
for (const string& a : wordDict) {
um[a]++;
}
res = dfs(0);
return res;
}
};
记忆递归的bool dfs()`的写法:
记录某个起点 x 是否“已经确定失败”,避免重复搜索。
mem[x] = 0:从 x 出发 无法拆分成功
mem[x] = 1:从 x 出发 可以拆分成功
class Solution {
private:
int x = 0;
unordered_map<string, int> um;
unordered_map<int, bool> mem;
int n = 0;
string s;
bool res = false;
bool dfs(int x) {
if (x >= n) {return true;}
if (mem.count(x)) {return mem[x];}
for (int i = x; i < n; i++) {
string t = s.substr(x, i-x+1);
if (um.count(t)) {
if (dfs(i+1)) {
mem[x] = true;
return true;
} else {
mem[x] = false;
}
}
}
return false;
}
public:
bool wordBreak(string s, vector<string>& wordDict) {
n = s.size();
this->s = s;
for (const string& a : wordDict) {
um[a]++;
}
res = dfs(0);
return res;
}
};

浙公网安备 33010602011771号