131. 分割回文串
思路分析
-
题目含义:给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
-
要找到所有可能的分割方案,显然这是一道 搜索题,可能还要用到 记忆化 搜索,可能还需要 剪枝 。
-
设
dfs(pos)
是用于搜索的函数,其中pos
是搜索到的位置。假设当前搜到了 s 的第i
个元素,且s[0:i-1]
已经被拆分成若干个回文串了。此时,我们需要确定往前走几步(即确定从当前位置i到其后位置j),这些字符构不构成回文串。即s[i:j]
构成字符串与否,如果s[i:j]
构成回文串,那么dfs(j+1)
,如果不构成:- 能否停止
j
向后遍历以剪枝?不能。因为如果s[i:j]
=ab
,那么仍然需要继续寻找下一个位置,因为s[i:j+1]
可能等于aba
。
- 能否停止
-
显然,当
pos
等于s.size()
时,一个分割方案完成。当j
遍历到s.size()-1
时,也不能得到一个回文串时,那么,此时需要 回溯 。 -
根据上述思路,我写出了我的答案。
我的答案
class Solution {
public:
vector<vector<string>> ret;
int n;
vector<string> cur;
vector<vector<string>> partition(string s) {
n = s.size();
dfs(0, s);
return ret;
}
void dfs(int pos, string &s){
if (pos == n){
ret.push_back(cur);
return ;
}
for (int i = pos; i < n; i ++){
string tmp = s.substr(pos, i-pos+1);
string r_tmp = tmp;
reverse(r_tmp.begin(), r_tmp.end());
if (tmp == r_tmp){
cur.push_back(tmp);
dfs(i+1, s);
cur.pop_back(); // 回溯
}
}
return ;
}
};
执行用时:128 ms, 在所有 C++ 提交中击败了86.17%的用户
内存消耗:74.1 MB, 在所有 C++ 提交中击败了51.43%的用户
LeetCode题解
-
LeetCode的题解思路和上述 搜索+回溯 的思路差不多。相比于我的答案,优化了回文串的判断方法。
-
从上述代码中可以看出,我判断回文串的方式是,将字符串反转(reverse),然后使用
==
比较,这种比较方式是O(n)
的时间复杂度。 -
LeetCode题解先对字符串 s 进行 预处理 , 使得判断回文串的时间复杂度降低为:O(1)。
-
预处理的方式是:使用 动态规划 。设
f[i][j]
表示s[i:j]
是否是回文串,可以得到转移方程:f[i][j] = { true, if i >= j; f[i+1][j-1] && s[i]==s[j], otherwise; }
-
根据上述思路,可以写出题解:
class Solution {
public:
int n;
vector<vector<string>> ret;
vector<string> cur;
vector<vector<bool>> f;
vector<vector<string>> partition(string s) {
n = s.size();
f.assign(n, vector<bool>(n, true));
for (int i = n-1; i >= 0; i --){
for (int j = i+1; j < n; j ++){
f[i][j] = f[i+1][j-1] && (s[i] == s[j]);
}
}
dfs(0, s);
return ret;
}
void dfs(int pos, string &s){
if (pos == n){
ret.push_back(cur);
return ;
}
for (int i = pos; i < n; i ++){
if (f[pos][i]){
cur.push_back(s.substr(pos, i-pos+1));
dfs(i+1, s);
cur.pop_back();
}
}
return ;
}
};
执行用时:124 ms, 在所有 C++ 提交中击败了87.45%的用户
内存消耗:74 MB, 在所有 C++ 提交中击败了62.14%的用户
2021.3.7 23:29