1、寻找字符串的最长回文子串 Longest Palindrome
(1)动态规划的方法
状态转移:dp[i][j]与dp[i+1][j-1]有关,如果dp[i+1][j-1]==true,并且A[i]==A[j],则dp[i][j]==true,单字符是true,双字符直接比较A[i]==A[j]。同时,需要注意的是初始化的dp[n][n]不能保证是初始化为false,需要在循环时确保false的情况。
class Solution {
public:
string longestPalindrome(string A) {
int n=A.size();
if(n<=1) return A;
int sum=1;
bool dp[n][n];//这里并没有让他全部初始化为false
for (int i = 0; i < n; ++i) {
dp[i][i] = true;
}
int left=0,right=0;
for(int i=n-1;i>=0;i--)
for(int j=i+1;j<n;j++)
{
if(A[i]==A[j]&&(i+1==j||dp[i+1][j-1]))
{
dp[i][j]=true;
if(sum<j-i+1){
sum=j-i+1;
left=i;
right=j;
}
}
else dp[i][j]=false;
}
return A.substr(left,right-left+1);
}
};
(2)中心拓展方法
class Solution {
public:
string longestPalindrome(string s) {
//使用第四个方法,自中心扩展
int start=0,end=0;//保存当前最长回文串的起始和终结位置
for(int i=0;i<s.size();i++)
{
int len1=expandFromCenter(s,i,i);//以当前字符为中心的回文串(奇数)
int len2=expandFromCenter(s,i,i+1);//以当前和下一个字符的中间为中心的回文串(偶数)
int len=max(len1,len2);
if(len>end-start+1)//如果求得的新字符串长度是比之前保存的要长的话
{
start=i-(len-1)/2;
end=i+len/2;
}
}
return s.substr(start,end-start+1);
}
int expandFromCenter(string& s,int left,int right){
while(left>=0&&right<=s.size()&&s[left]==s[right]){//这种书写方式考虑到奇数和偶数的情况
--left;
++right;
}
return right-left-1;//返回长度,因为right和left都是移到了回文串的外围位置
}
};
2、回文字符串划分 131. Palindrome Partitioning
题目描述:
将一个字符串划分成回文字串,返回所有的划分结果
分析:
从左到右,当找到一个回文字串时,递归下去,直到字符串的末尾,把得到的串加到结果上。递归后置pop操作。是通常的回溯结构
class Palindrome{
public:
/*131. Palindrome Partitioning
Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
For example, given s = "aab",
Return
[
["aa","b"],
["a","a","b"]
]
*/
public:
vector<vector<string>> partition(string s) {
vector<vector<string>> res;
vector<string> curStr;
doPartition(s,0,s.size(),curStr,res);
return res;
}
void doPartition(string& s,int left,int length,vector<string>& curStr,vector<vector<string>>& res){
if(left==length)
{
res.push_back(curStr);
return ;
}
for(int i=left;i<length;i++)
{
if(isPalindrome(s,left,i))
{
curStr.push_back(s.substr(left,i-left+1));//注意substr的写法
doPartition(s,i+1,length,curStr,res);
curStr.pop_back();
}
}
}
bool isPalindrome(string& s,int left,int right){
if(left==right)
return true;
while(left<=right)
{
if(s[left++]!=s[right--])
return false;
}
return true;
}
};
3、回文字符串划分 Palindrome Partitioning II 程序员代码面试 回文最少分割数
题目:
将一个字符串划分成回文子串,要求返回最少的划分次数
思路:
参考程序员代码面试指南
用dp[i]表示str[i----length-1]的最少划分数,而dp[i]取决于从i开始的第一个 这个题目与第一个题目类似
int minCut(string s) {
int n=s.size();
vector<int> dp(n+1,numeric_limits<int>::max());//从i到n-1的最少分割数,结果返回dp[0]
dp[n]=-1;//这个很巧妙
vector<vector<int>> p(n,vector<int>(n,0));//p[i][j]表示从i-j是否是回文串
for(int i=n-1;i>=0;i--)
{
for(int j=i;j<n;j++)
{
if(s[i]==s[j]&&(j-i<2||p[i+1][j-1]==1)) {
p[i][j]=1;
dp[i]=min(dp[i],1+dp[j+1]);//注意时j+1很巧妙
}
}
}
return dp[0];
}
4、最长回文子序列 516. Longest Palindromic Subsequence
if i == j, then longest[i][j] = 1, naturally
if i+1 == j, then longest[i][j] = 2 if s[i] == s[j]
longest[i][j] = 1 otherwise
Transition rule:
- s[i] == s[j]
dp[i][j] = max(dp[i+1][j], dp[i][j-1], dp[i+1][j-1] + 2) - s[i] != s[j]
dp[i][j] = max(dp[i+1][j], dp[i][j-1], dp[i+1][j-1])
class Solution {
public:
int longestPalindromeSubseq(string s) {
int n=s.size();
vector<vector<int>> dp(n,vector<int>(n,0));
for(int i=n-1;i>=0;i--)
{
dp[i][i]=1;
for(int j=i+1;j<n;j++)
{
if(s[i]==s[j])
dp[i][j]=dp[i+1][j-1]+2;
dp[i][j]=max(dp[i][j],max(dp[i+1][j],dp[i][j-1]));
}
}
return dp[0][n-1];
}
};
5、回文子串的个数 647. Palindromic Substrings
这道题与1一样,也是设置dp[][] 布尔矩阵,只是,在循环过程中统计true出现的次数
class Solution {
public:
int countSubstrings(string A) {
int n=A.size();
if(n<=1) return n;
int sum=0;
bool dp[n][n];//这里并没有让他全部初始化为false
for (int i = 0; i < n; ++i) {
dp[i][i] = true;
sum++;
}
int left=0,right=0;
for(int i=n-1;i>=0;i--)
for(int j=i+1;j<n;j++)
{
if(A[i]==A[j]&&(i+1==j||dp[i+1][j-1]))
{
dp[i][j]=true;
sum++;
}
else dp[i][j]=false;
}
return sum;
}
};