题目
知识点
- 记忆化搜索
思路
-
虽然是一个个单词,但本质可以看作是路径寻找问题,字符串s每个字符相当于一个路径节点,问是否能从以第一个字母为节点开始,找到最后一个字符结束的路径,如下为例子:
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
"apple"相当于0->4有一条路,8->12有一条路(单词可重复使用)
"pen"相当于5->7有一条路 -
那么,在上述的例子中,我们可以找到一条路径满足问题,即0->4 再到 5->7 再到 8->12
-
问题在于如何获得每个单词能够产生的“路径”。注意,例如上述中的例子apple可以出现两次(因为相同单词可以使用多次)。因此,在开始的时候先构建路径图。我们对于一个单词,需要从头到尾搜索一遍(Line 29-46),检索在
s
中的位置。因为find()
只能找到第一个子串,所以用while
设置循环,每次寻找子串开始位置向后一位继续查找。我使用了map<int, vector<int>> m
记录了所有可能走的路径,在后面DFS找路径的时候,m[当前位置]
即可获得这个位置出发,所有能够到达的位置。 -
但是,但是… 单一这样的方法只能过30+数据,不能过全部的数据,因此我们还需要进行优化。
记忆化搜索
- 其实就是动态规划那味…
- 使用
int vis[1000]
去记录当前这个位置是否被访问过(-1 - 未访问,0 - 访问过但走不通,1-访问了走得通)。即:如果被访问了,当前位置出发是否能够到达。 - 当然在这道题里找到了就逐层返回true了(可以用两位表示,这里我就直接用
int
类型了)…所以在这题里,vis
就是用于告诉我们该位置已经走过了,是走不通的,别走了别走了!浪费时间(Line 9) -
Line 14-21 就是向下搜索的过程,如果当前位置可以继续往下走
m.count(depth)
,那我们就一个个向下搜索,如果全部向下走都不行,那我们从该位置出发就是行不通的,因此把这个位置vis
标记为0 (Line 23)
代码
- PS:我的方法代码虽然长一点,但是速度好像比一般的更快哈哈~
class Solution {
public:
map<int, vector<int>> m; // index - 位置
int ed;
int vis[1000];
bool dfs(int depth) {
int &ans = vis[depth];
if (ans != -1) return ans; // 这个depth后续访问已经做过,是走不通的
if (depth == ed) return true;
if (depth > ed) return false;
if (m.count(depth)) {
for (int i:m[depth]) {
if (dfs(i + 1)) {
ans = 1; // 这个depth可以走
return true;
}
}
}
ans = 0;
return false;
}
bool wordBreak(string s, vector<string> &wordDict) {
ed = s.size();
memset(vis, -1, sizeof(int[1000]));
for (const string &i:wordDict) {
int b = 0, index;
bool end = false;
while (!end) {
if (b > s.size()) break;
index = s.find(i, b);
if (index != -1) {
if (!m.count(index)) {
m[index] = vector<int>(1, index + i.size() - 1);
} else {
m[index].emplace_back(index + i.size() - 1);
}
b += 1;
} else {
end = true;
}
}
}
return dfs(0);
}
};