代码分析
题目一
暴力法递归
递归分割所有情况,判断分割部分是否是回文串,如果分割部分是回文串,继续分割后面的字符串,直到能全部分割为止,为一种分割情况
class Solution {
public:
vector<vector<string>>res;
bool isIsPalindrome(string &s, int left, int right){//判断是否是回文
while(left<right&&s[left]==s[right])
{
left++;
right--;
}
return left>=right;
}
void dfs(string &s, int index, vector<string>&vec){//递归查找
if(index == s.size()){//分割至最后,则能分割完
res.push_back(vec);
return;
}
for(int i = index; i < s.size(); i++){
if(isIsPalindrome(s, index, i)){//如果从此处分割是回文串,查找下一分割点
vec.push_back(s.substr(index, i-index+1));
dfs(s, i+1, vec);
vec.pop_back();
}
}
}
vector<vector<string>> partition(string s) {
vector<string>vec;//存储每种分割情况数组
dfs(s,0,vec);
return res;
}
};
递归优化
不用对每个分割的字符串判断一次是否是回文串
用中心扩展法或动态规划法,找出所有的回文子串,并记录,当判断分割字符串是回文串时,直接用记录判断
class Solution {
public:
vector<vector<string>>res;
void isIsPalindrome(string &s, vector<vector<bool>>&checkPartition){//生成回文子串记录
for(int i = 0; i < s.size(); i++){
int left = i,right = i;
while(left>=0&&right<s.size()&&s[left]==s[right]){
checkPartition[left--][right++] = 1;
}
left = i,right = i+1;
while(left>=0&&right<s.size()&&s[left]==s[right]){
checkPartition[left--][right++] = 1;
}
}
}
void dfs(string &s, int index, vector<string>&vec,vector<vector<bool>>&checkPartition){//递归查找
if(index == s.size()){
res.push_back(vec);
return;
}
for(int i = index; i < s.size(); i++){
if(checkPartition[index][i]){
vec.push_back(s.substr(index, i-index+1));
dfs(s, i+1, vec,checkPartition);
vec.pop_back();
}
}
}
vector<vector<string>> partition(string s) {
vector<vector<bool>>checkPartition(s.size(),vector<bool>(s.size(),0));//记录数组
isIsPalindrome(s,checkPartition);
vector<string>vec;
dfs(s,0,vec,checkPartition);
return res;
}
};
题目二
记忆化搜索
回溯,从后往前找当前位置所需形成回文的最小分割次数,并保存在记忆数组中
class Solution {
public:
bool isPalindrome(string &s,int left,int right){//判断是否是回文串
while(left<right){
if(s[left]!=s[right]){
return false;
}
left++;
right--;
}
return true;
}
int dfs(string &s, int index, vector<int>&memo){//递归
if(index==s.size()){//如果遍历到最后,由于分割次数比回文串个数少1,遍历到最后要-1
return -1;
}
if(memo[index]!=-1){//记忆化数组
return memo[index];
}
int k = INT_MAX;
for(int i = index; i < s.size(); i++){
if(isPalindrome(s,index,i)){//如果是回文,继续分割,返回最短分割次数+1
k = min(k, dfs(s,i+1,memo)+1);
}
}
memo[index] = k;
return k;
}
int minCut(string s) {
vector<int>memo(s.size()+1,-1);
return dfs(s,0,memo);
}
};
记忆化搜索优化
不用对每个分割的字符串判断一次是否是回文串
用中心扩展法或动态规划法,找出所有的回文子串,并记录,当判断分割字符串是回文串时,直接用记录判断
class Solution {
public:
void isPalindrome(string &s,vector<vector<bool>>&palindrome){
for(int i = 0; i < s.size(); i++){
int left = i,right=i;
while(left>=0&&right<s.size()&&s[left]==s[right]){
palindrome[left--][right++]=1;
}
left = i; right=i+1;
while(left>=0&&right<s.size()&&s[left]==s[right]){
palindrome[left--][right++]=1;
}
}
}
int dfs(string &s, int index, vector<int>&memo,vector<vector<bool>>&palindrome){
if(index==s.size()){
return -1;
}
if(memo[index]!=-1){
return memo[index];
}
int k = INT_MAX;
for(int i = index; i < s.size(); i++){
if(palindrome[index][i]){
k = min(k, dfs(s,i+1,memo,palindrome)+1);
}
}
memo[index] = k;
return k;
}
int minCut(string s) {
vector<vector<bool> >palindrome(s.size(),vector<bool>(s.size(),0));
isPalindrome(s,palindrome);
vector<int>memo(s.size()+1,-1);
return dfs(s,0,memo,palindrome);
}
};
class Solution {
public:
void isPalindrome(string &s,vector<vector<bool>>&palindrome){
for(int right = 0; right < s.size(); right++){
for(int left = 0; left <= right; left++){
if((right-left<=2||palindrome[left+1][right-1])&&s[left]==s[right]){
palindrome[left][right]=1;
}
}
}
}
int dfs(string &s, int index, vector<int>&memo,vector<vector<bool>>&palindrome){
if(index==s.size()){
return -1;
}
if(memo[index]!=-1){
return memo[index];
}
int k = INT_MAX;
for(int i = index; i < s.size(); i++){
if(palindrome[index][i]){
k = min(k, dfs(s,i+1,memo,palindrome)+1);
}
}
memo[index] = k;
return k;
}
int minCut(string s) {
vector<vector<bool> >palindrome(s.size(),vector<bool>(s.size(),0));
isPalindrome(s,palindrome);
vector<int>memo(s.size()+1,-1);
return dfs(s,0,memo,palindrome);
}
};
动态规划优化
dp[i]代表当前位置最少分割次数
状态转移方程如下:
dp[i] = min(s[j ~ i]是回文?dp[j])(for j in range(i) )
用中心扩展法或动态规划法,找出所有的回文子串,并记录,当判断分割字符串是回文串时,直接用记录判断
class Solution {
public:
void isPalindrome(string &s,vector<vector<bool>>&palindrome){//中心扩展
for(int i = 0; i < s.size(); i++){
int left = i-1,right=i+1;
palindrome[i][i]=1;
while(left>=0&&right<s.size()&&s[left]==s[right]){
palindrome[left--][right++]=1;
}
left = i; right=i+1;
while(left>=0&&right<s.size()&&s[left]==s[right]){
palindrome[left--][right++]=1;
}
}
}
int minCut(string s) {
vector<vector<bool> >palindrome(s.size(),vector<bool>(s.size(),0));
isPalindrome(s,palindrome);
vector<int>dp(s.size()+1,0);
for(int i = 0; i < s.size(); i++){
if(palindrome[0][i]){
dp[i] = 0;
continue;
}
dp[i] = i;
for(int j = 1; j <= i; j++){
if(palindrome[j][i]){
dp[i] = min(dp[i],dp[j-1]+1);
}
}
}
return dp[s.size()-1];
}
};
class Solution {
public:
void isPalindrome(string &s,vector<vector<bool>>&palindrome){//动态规划
for(int right = 0; right < s.size(); right++){
for(int left = 0; left <= right; left++){
if((right-left<=2||palindrome[left+1][right-1])&&s[left]==s[right]){
palindrome[left][right]=1;
}
}
}
}
int minCut(string s) {
vector<vector<bool> >palindrome(s.size(),vector<bool>(s.size(),0));
isPalindrome(s,palindrome);
vector<int>dp(s.size()+1,0);
for(int i = 0; i < s.size(); i++){
if(palindrome[0][i]){//如果本身是回文串不需要分割,可以节省时间
dp[i] = 0;
continue;
}
dp[i] = i;//最坏情况每个字符为一个回文串
for(int j = 1; j <= i; j++){
if(palindrome[j][i]){//j~i是回文
dp[i] = min(dp[i],dp[j-1]+1);//j的上一个位置最少分割次数+1作为i位置的一种分割次数
}
}
}
return dp[s.size()-1];
}
};