题目描述
解法1:回溯
题目要求列出所有符合的情况,不满足回文的直接剪枝,很适合回溯。
class Solution {
private:
vector<vector<string> > ans;
vector<string> elem;//ans中的成员
int len;
//检查是否是回文字符串
bool check(const string& str, int left, int right)
{
while(left < right)
{
if(str[left] != str[right])
return false;
++left;
--right;
}
return true;
}
//DFS递归回溯
void DFS(const string& s, int start)
{
if(start == len)
{
ans.push_back(elem);
return;
}
for(int i = start; i < len; i++)
{
if(! check(s, start, i))//[start,i]注意是闭区间
continue;
elem.push_back(s.substr(start, i - start + 1));
DFS(s, i + 1);
elem.pop_back();//回溯
}
}
public:
vector<vector<string>> partition(string s) {
len = s.size();
if(len == 0) return ans;
DFS(s, 0);
return ans;
}
};
解法2:动态规划
也可以利用动态规划把结果先算出来,这样就可以以 O(1) 的时间复杂度直接得到一个子串是否是回文,也就是用空间换取时间
class Solution {
private:
vector<vector<string> > ans;
vector<string> elem;//ans中的成员
int len;
//DFS递归回溯
void DFS(const string& s, int start, vector<vector<bool> >dp)
{
if(start == len)
{
ans.push_back(elem);
return;
}
for(int i = start; i < len; i++)
{
if(! dp[start][i])//[start,i]注意是闭区间
continue;
elem.push_back(s.substr(start, i - start + 1));
DFS(s, i + 1, dp);
elem.pop_back();//回溯
}
}
public:
vector<vector<string>> partition(string s) {
len = s.size();
if(len == 0) return ans;
//利用动态规划把结果先算出来,这样就可以 O(1) 的时间复杂度直接得到一个子串是否是回文。
vector<vector<bool> > dp(len, vector<bool>(len, false));
// 状态:dp[i][j] 表示 s[i][j] 是否是回文
// 状态转移方程:在 s[i] == s[j] 的时候,dp[i][j] 参考 dp[i + 1][j - 1]
//memset(dp, false, sizeof dp);
for (int right = 0; right < len; right++)
{
// 注意:left <= right 取等号表示 1 个字符的时候也需要判断
for (int left = 0; left <= right; left++)
{
if(s[left] == s[right] && (right - left < 3 || dp[left + 1][right - 1]))
dp[left][right] = true;
}
}
DFS(s, 0, dp);
return ans;
}
};
对动态规划不理解的话,可以先去看看 【动态规划】LeetCode - 5. 最长回文子串
但本题用动态规划的运行时间远大于回溯,我感觉是因为虽然原理上是暴力,还有很多重复计算,但是本题回溯法的剪枝很厉害,省去很多是否回文的判定