1.最长回文子串
中心遍历法:
1.外层循环遍历字符串,以每个字符串为中心点,向两边扩展,判断分为以下几种情况
s[left]==s[i] : 表示左边和中心点相等 left–
s[right]==s[i]:表示右边和中心点相等 right++
s[left]==s[right]:表示左边和右边相等 left-- right++
经过几次判断之后,left指向回文串的首部的前一个位置,right指向回文串尾部的后一个位置 -> 回文串长度= right-left-1
class Solution {
public:
string longestPalindrome(string s) {
int max=0;
string str;
for(int i=0;i<s.size();i++)
{
int left=i;
int right=i;
while(left>=0&&s[left]==s[i])
left--;
while(right<s.size()&&s[right]==s[i])
right++;
while(left>=0&&right<s.size()&&s[left]==s[right])
{
left--;
right++;
}
int len=right-left-1;//回文串长度
if(len>max)
{
max=len;
string temp(s.begin()+left+1,s.begin()+right);
str=temp;
}
}
return str;
}
};
动态规划法:
设dp[i][j] 表示从第j个字符至第i个字符是否是回文字符串,-> 当i==j时只有一个字符,则是字符串
当s[i]==s[j]时表示字符串的两端是相等的,那么dp[i-1][j+1]是回文串,那么dp[i][j]也是回文串
此时需要注意的是:当s[i]==s[j]并且只有两个或者三个字符时,说明是回文串,即i-1<=j+1时表示只有两个或者三个字符串
又我们是从短的字符串开始向后推的,即长的字符串的状态是需要短子字符串的状态来确定的,因此第一层循环时从前往后,第二层循环时从后往前
class Solution {
public:
string longestPalindrome(string s) {
//dp[i][j]表示i-j是否为回文串
//-> s[i]==s[j]&&dp[i][j-1]==true 则是回文串
//当只有一个字符时,肯定是回文串
//当只有两个字符时,则需要判断这两个字符是否相等
vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));
for(int i=0;i<s.size();i++)//字符串逐渐边长
{
for(int j=i;j>=0;j--)
{
if(i==j)//只有一个字符
dp[i][j]=true;
else if(s[i]==s[j]&&(j+1>=i-1||dp[i-1][j+1]==true))//j+1>=i-1表示只有2个或者3字符
dp[i][j]=true;
}
}
int left=0;
int right=0;
int max=0;
for(int i=0;i<s.size();i++)
{
for(int j=i;j>=0;j--)
{
if(dp[i][j]==true)
{
if(i-j+1>max)
{
max=i-j+1;
left=j;
right=i;
}
}
}
}
string str(s.begin()+left,s.begin()+right+1);
return str;
}
};
2.最长回文子序列
给定一个字符串 s
,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s
的最大长度为 1000
。
注:回文子序列不是子串,因此可以不是连续的
class Solution {
public:
int longestPalindromeSubseq(string s) {
//dp[i][j]表示j-i的回文串之中字符的个数
vector<vector<int>> dp(s.size(),vector<int>(s.size(),0));
int max=0;
for(int i=0;i<s.size();i++)
{
for(int j=i;j>=0;j--)
{
if(i==j)
dp[i][j]=1;//一个字符时
else if(s[i]==s[j])
dp[i][j]=dp[i-1][j+1]+2;//中间回文串的个数+2
else//不是回文串
dp[i][j]=fmax(dp[i][j+1],dp[i-1][j]);//不在i-j区间之内,因此往上返回一层
max=fmax(max,dp[i][j]);
}
}
return max;
}
};
3.查找两个字符串a,b中的最长公共子串
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string str1;
string str2;
while(cin>>str1)
{
cin>>str2;
if(str1.size()<str2.size())
str1.swap(str2);//保证str1为较长串
int len1=str1.size();
int len2=str2.size();
string str;
vector<vector<int>>dp(len2+1,vector<int>(len1+1,0));
for(int i=0;i<len2;i++)
{
for(int j=0;j<len1;j++)
{
if(str2[i]==str1[j])
{
dp[i+1][j+1]=dp[i][j]+1;
if(dp[i+1][j+1]>str.size())//新的较长子串
{
int begin=i-dp[i+1][j+1]+1;//相同子串的起始位置
string temp(str2.begin()+begin,str2.begin()+i+1);
str.swap(temp);
}
}
else
dp[i+1][j+1]=0;//不相等
}
}
cout<<str<<endl;
}
return 0;
}
空间优化:
本层只和上一层有关,既可以进行空间优化
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string str1;
string str2;
while(cin>>str1)
{
cin>>str2;
if(str1.size()<str2.size())
str1.swap(str2);//保证str1为较长串
int len1=str1.size();
int len2=str2.size();
string str;
vector<int>dp(len1+1,0);
for(int i=0;i<len2;i++)
{
for(int j=len1-1;j>=0;j--)//为了避免数据覆盖,从后往前进行遍历
{
if(str2[i]==str1[j])
{
dp[j+1]=dp[j]+1;
if(dp[j+1]>str.size())//新的较长子串
{
int begin=i-dp[j+1]+1;//相同子串的起始位置
string temp(str2.begin()+begin,str2.begin()+i+1);
str.swap(temp);
}
}
else
dp[j+1]=0;//不相等
}
}
cout<<str<<endl;
}
return 0;
}