1.题目(最长回文子串)
Given a string s, return the longest palindromic substring in s.
Example 1:
Input: s = "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: s = "cbbd"
Output: "bb"
Example 3:
Input: s = "a"
Output: "a"
Example 4:
Input: s = "ac"
Output: "a"
2.思路
2.1迭代法(暴力法)
循环遍历,找以S[i]为中心的最大回文子串,最后遍历一遍字符串找最大回文子串即可,复杂度O(N^2)
3.代码
3.1 迭代法
class Solution {
public:
string maxrev(string s,int a,int b)
{
while((a>=0)&&(b<s.size())&&(s[a]==s[b]))
{
a--;
b++;
}
// cout<<a<<"a"<<" "<<b<<"b"<<"kk";
// cout<<s.substr(a+1,b-a-1)<<endl;
return s.substr(a+1,b-a-1);
}
string longestPalindrome(string s)
{
//遍历法
int num=s.size();
string maxs="";
for(int i=0;i<num;i++)
{
string s1=maxrev(s,i,i);
string s2=maxrev(s,i,i+1);
if(maxs.size()<s1.size())
maxs=s1;
if(maxs.size()<s2.size())
maxs=s2;
}
return maxs;
}
};
3.2 dp法
class Solution {
public:
string longestPalindrome(string s) {
int dp[1005][1005];
int num=s.size();
for(int i=0;i<num;i++)
{
int j=i;
dp[i][j]=1;
}
for(int i=0;i<num-1;i++)
{
int j=i+1;
if(s[i]==s[j])
dp[i][j]=1;
else dp[i][j]=0;
}
for(int i=num-3;i>=0;i--)
{
for(int j=i+2;j<num;j++)
{
if(s[i]==s[j])
dp[i][j]=dp[i+1][j-1];
else
dp[i][j]=0;
}
}
int max=0;int a=0;int b=0;
for(int i=0;i<s.size();i++)
{
for(int j=0;j<s.size();j++)
{
// cout<<dp[i][j]<<" ";
if((dp[i][j]==1)&&(max<j-i+1))
{
max=j-i+1;
a=i;
b=j;
}
}
// cout<<endl;
}
return s.substr(a,b-a+1);
}
};
4.题目二(最长回文子序列)
Given a string s, find the longest palindromic subsequence’s length in s.
A subsequence is a sequence that can be derived from another sequence by deleting some or no elements without changing the order of the remaining elements.
Example 1:
Input: s = "bbbab"
Output: 4
Explanation: One possible longest palindromic subsequence is "bbbb".
Example 2:
Input: s = "cbbd"
Output: 2
Explanation: One possible longest palindromic subsequence is "bb".
5.思路(二维dp)
我们说这个问题对 dp 数组的定义是:在子串s[i…j]中,最长回文子序列的长度为dp[i][j]。一定要记住这个定义才能理解算法。
具体来说,如果我们想求dp[i][j],假设你知道了子问题dp[i+1][j-1]的结果(s[i+1…j-1]中最长回文子序列的长度),你是否能想办法算出dp[i][j]的值(s[i…j]中,最长回文子序列的长度)呢?
可以!这取决于s[i]和s[j]的字符:
如果它俩相等,那么它俩加上s[i+1…j-1]中的最长回文子序列就是s[i…j]的最长回文子序列:
如果它俩不相等,说明它俩不可能同时出现在s[i…j]的最长回文子序列中,那么把它俩分别加入s[i+1…j-1]中,看看哪个子串产生的回文子序列更长即可
以上两种情况写成代码就是这样:
if (s[i] == s[j])
// 它俩一定在最长回文子序列中
dp[i][j] = dp[i + 1][j - 1] + 2;
else
// s[i+1..j] 和 s[i..j-1] 谁的回文子序列更长?
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
至此,状态转移方程就写出来了,根据 dp 数组的定义,我们要求的就是dp[0][n - 1],也就是整个s的最长回文子序列的长度。
【注意】!!!首先明确一下 base case,如果只有一个字符,显然最长回文子序列长度是 1,也就是dp[i][j] = 1,(i == j)。
因为i肯定小于等于j,所以对于那些i > j的位置,根本不存在什么子序列,应该初始化为 0。
6.dp代码
dp[i][j]对应关系一定画图,不要想当然!!!不然很容易对应错
另外状态转换关系是加2!!!
class Solution {
public:
int longestPalindromeSubseq(string s)
{
int num=s.size();
int dp[1005][1005];
memset(dp,0,sizeof(dp));
//fill(dp[0],dp[0]+1005*1005,0);
//basecase
for(int i=0;i<num;i++)
{
int j=i;
dp[i][j]=1;
}
//开始反着遍历
for(int i=num-2;i>=0;i--)
{
for(int j=i+1;j<num;j++)
{
if(s[i]==s[j])
dp[i][j]=dp[i+1][j-1]+2;
else
dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
}
}
return dp[0][num-1];
}
};