5. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
思路:
方法一:动态规划
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示以位置
i
i
i 开始、
j
j
j 结束的子串是否为回文串,长度
l
e
n
=
j
−
i
+
1
len=j-i+1
len=j−i+1 。
1.边界条件:若
l
e
n
=
1
len=1
len=1,则必为回文串,
d
p
[
i
]
[
j
]
=
1
dp[i][j]=1
dp[i][j]=1。若
l
e
n
=
2
len=2
len=2,则是否为回文串看
s
[
i
]
s[i]
s[i]是否等于
s
[
j
]
s[j]
s[j],即
d
p
[
i
]
[
j
]
=
(
s
[
i
]
=
=
s
[
j
]
)
dp[i][j]=(s[i]==s[j])
dp[i][j]=(s[i]==s[j])。
2.状态转移方程:
d
p
[
i
]
[
j
]
=
d
p
[
i
+
1
]
[
j
−
1
]
&
&
(
s
[
i
]
=
=
s
[
j
]
)
dp[i][j]=dp[i+1][j-1] \&\& (s[i]==s[j])
dp[i][j]=dp[i+1][j−1]&&(s[i]==s[j]),即若
d
p
[
i
+
1
]
[
j
−
1
]
dp[i+1][j-1]
dp[i+1][j−1]是回文串,且
s
[
i
]
=
s
[
j
]
s[i]=s[j]
s[i]=s[j],则
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为回文串。
方法二:两边扩展法
1.对于位置
i
i
i,向两边扩展,直到字符串
s
.
s
u
b
s
t
r
(
l
e
f
t
,
r
i
g
h
t
−
l
e
f
t
+
1
)
s.substr(left,right-left+1)
s.substr(left,right−left+1)不再是回文字符串。
2.对于扩展方法,有两种可能。①
s
[
i
]
=
s
[
i
+
1
]
s[i]=s[i+1]
s[i]=s[i+1]。②
s
[
i
]
=
s
[
i
+
2
]
s[i]=s[i+2]
s[i]=s[i+2]
代码:
//方法1:动态规划
string longestPalindrome1(string s) {
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n));
string ans;
//以长度为第一层循环,因为每一个当前长度的子串的状态判断需要依靠更短长度的子串的状态
//因此按照长度由小到大的顺序判断
for (int l = 1; l <= n; ++l) {
for (int i = 0; i + l-1 < n; ++i) { //起始位置
int j = i + l-1; //结束位置
if (l == 1) {
dp[i][j] = 1;
}
else if (l == 2) {
dp[i][j] = (s[i] == s[j]);
}
else {
dp[i][j] = (s[i] == s[j] && dp[i + 1][j - 1]);
}
if (dp[i][j] ) { //判断其实回文串
if( l > ans.size()) //判断其长度
ans = s.substr(i, l );
}
}
}
return ans;
}
//方法2:两边扩展法
pair<int,int> getLongestSubPali(string s, int i, int j ) //注意pair返回类型的语法
{
while(i>=0 && j<s.length() && s[i]==s[j])
{
i--;
j++;
}
return {i+1, j-1} ; //花括号
}
string longestPalindrome(string s) {
int n=s.length();
int start=0,end=0;
string res;
for(int i=0;i<n;i++)
{
auto [left1, right1]=getLongestSubPali(s, i, i+1 );
if(right1-left1>end-start)
{
start=left1;
end=right1;
}
auto [left2, right2]=getLongestSubPali(s, i, i+2 );
if(right2-left2>end-start)
{
start=left2;
end=right2;
}
}
res=s.substr(start,end-start+1);
return res;
}
结果:
647. 回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:“abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”
示例 2:
输入:“aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”
提示:
输入的字符串长度不会超过 1000 。
代码:
class Solution {
public:
int countSubstrings(string s) {
int n=s.length();
if(n<=1)
return n;
int count=0;
vector<vector<int> > dp(n,vector<int>(n));
for(int l=1;l<=n;l++)
{
for(int i=0;l-1+i<n;i++)
{
int j=l-1+i;
if(l==1)
{
dp[i][j]=1;
count++;
}
else if(l==2)
{
if(s[i]==s[j])
{
dp[i][j]=1;
count++;
}
}
else
{
if(s[i]==s[j])
{
dp[i][j]=dp[i+1][j-1];
if(dp[i][j])
count++;
}
}
}
}
return count;
}
};
结果:
参考链接:
[1] 力扣官方题解:最长回文子串