题目
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是 回文串。返回 s
所有可能的分割方案。
思路
分割s,从字串的长度为1开始分割,直至字串的长度为字符串的长度结束,在字符串上根据不同的字串长度按序依次进行分割
class Solution {
public:
vector<vector<string>> res;
vector<string> vec;
string path;
bool IsPalindromeString(string s)
{
int left = 0;
int right = s.length() - 1;
while (left < right) {
if (s[left] != s[right]) {
return false;
}
++left;
--right;
}
return true;
}
bool partitionAux(string s, int substr_length)
{
int i;
for(i = 0; i < s.length(); i += substr_length)
{
path = s.substr(i, substr_length);
if(IsPalindromeString(path)) vec.emplace_back(path);
else return false;
}
return true;
}
vector<vector<string>> partition(string s) {
for(int i = 1; i <= s.length(); ++i)
{
vec.clear(); path.clear();
if(partitionAux(s, i)) res.emplace_back(vec);
}
return res;
}
};
错了,做了一个小时多一点,忽略了最关键的一点,在字串长度的条件下,可以有多种组合情况,而不是按照顺序一个字串一个字串的组合
自己又看了半个小时
看代码随想录
class Solution {
public:
vector<vector<string>> res;
vector<string> vec;
string path;
bool IsPalindromeString(string s)
{
int left = 0;
int right = s.length() - 1;
while (left < right) {
if (s[left] != s[right]) {
return false;
}
++left;
--right;
}
return true;
}
void partitionAux(string s, int start_index)
{
for(int i = 1; i <= s.length() - start_index; ++i)
{
path = s.substr(start_index, i);
if(IsPalindromeString(path)) vec.emplace_back(path);
else return;
if(start_index + i == s.length())
{
res.emplace_back(vec);
vec.pop_back();
}
else partitionAux(s, start_index + i);
vec.pop_back();
}
}
vector<vector<string>> partition(string s) {
partitionAux(s, 0);
return res;
}
};
完全不知道题意,对切割问题没有概念
优化
对回文的判断进行优化,上述代码isPalindrome
函数运用双指针的方法来判定对于一个字符串s
, 给定起始下标和终止下标, 截取出的子字符串是否是回文字串。但是其中有一定的重复计算存在:
例如给定字符串"abcde"
, 在已知"bcd"
不是回文字串时, 不再需要去双指针操作"abcde"
而可以直接判定它一定不是回文字串。
具体来说, 给定一个字符串s
, 长度为n
, 它成为回文字串的充分必要条件是s[0] == s[n-1]
且s[1:n-1]
是回文字串。
大家如果熟悉动态规划这种算法的话, 我们可以高效地事先一次性计算出, 针对一个字符串s
, 它的任何子串是否是回文字串, 然后在我们的回溯函数中直接查询即可, 省去了双指针移动判定这一步骤.
class Solution {
private:
vector<vector<string>> result;
vector<string> path; // 放已经回文的子串
vector<vector<bool>> isPalindrome; // 放事先计算好的是否回文子串的结果
void backtracking (const string& s, int startIndex) {
// 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
if (startIndex >= s.size()) {
result.push_back(path);
return;
}
for (int i = startIndex; i < s.size(); i++) {
if (isPalindrome[startIndex][i]) { // 是回文子串
// 获取[startIndex,i]在s中的子串
string str = s.substr(startIndex, i - startIndex + 1);
path.push_back(str);
} else { // 不是回文,跳过
continue;
}
backtracking(s, i + 1); // 寻找i+1为起始位置的子串
path.pop_back(); // 回溯过程,弹出本次已经添加的子串
}
}
void computePalindrome(const string& s) {
// isPalindrome[i][j] 代表 s[i:j](双边包括)是否是回文字串
isPalindrome.resize(s.size(), vector<bool>(s.size(), false)); // 根据字符串s, 刷新布尔矩阵的大小
for (int i = s.size() - 1; i >= 0; i--) {
// 需要倒序计算, 保证在i行时, i+1行已经计算好了
for (int j = i; j < s.size(); j++) {
if (j == i) {isPalindrome[i][j] = true;}
else if (j - i == 1) {isPalindrome[i][j] = (s[i] == s[j]);}
else {isPalindrome[i][j] = (s[i] == s[j] && isPalindrome[i+1][j-1]);}
}
}
}
public:
vector<vector<string>> partition(string s) {
result.clear();
path.clear();
computePalindrome(s);
backtracking(s, 0);
return result;
}
};