Fork me on GitHub

最长回文子串

问题描述:

给出一个字符串S,求S的最长回文子串的长度。

例:字符串“ASDFGHGFDSB”的最长回文子串为“SDFGHGFDS”,长度为9。

显然暴力解法的不仅复杂度较大,而且超级繁琐,采用动态规划可以更好的解决这类问题

令dp[i][j]表示S[i]至S[j]所表示的子串是否回文,是则为1,不是则为0.这样可以根据S[i]和S[j]是否相等分为两种情况:

  1. 如果S[i]==S[j],那么只要S[i+1]至S[j-1]是回文子串,那么S[i]至S[j]也是回文子串;如果S[i+1]至S[j-1]不是回文子串,那么S[i]至S[j]也不是回文子串;
  2. 如果S[i]!=S[j],那么S[i]至S[j]一定不是回文子串
    边界:dp[i][i]=1, dp[i][i+1]=(S[i]==S[i+1])?1:0

注意:如果按照i和j从小到大的顺序来枚举子串的端点,然后更新dp[i][j],这样会无法保证dp[i+1][j-1]已经得到计算,从而无法进行状态转移

因为边界的子串长度为1和2,所以可以考虑按照子串的长度和初始位置进行枚举,即第一遍将子串长度为3的dp值全部求出来,第二遍计算长度为4的子串可以通过计算出的长度为3的子串的dp值进行计算。这样问题就可以得到解决

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<cstdio>
#include<cstring>
const int maxn = 10010;
char S[maxn];
int dp[maxn][maxn];
int main()
{
gets(S);
int len = strlen(S);
int ans = 1; //初始化最长回文子串长度为1
memset(dp, 0, sizeof(dp));
for (int i = 0; i < len; i++)
{
dp[i][i] = 1;
if (i < len - 1)
{
if (S[i] == S[i + 1])
{
dp[i][i + 1] = 1;
ans = 2;
}
}
}
//状态转移方程
for (int L = 3; L <= len; L++)
{
for (int i = 0; i + L - 1 < len; i++)
{
int j = i + L - 1;
if (S[i] == S[j] && dp[i + 1][j - 1] == 1)
{
dp[i][j] = 1;
ans = L;
}
}
}
printf("%d\n", ans);
}

操作实例

https://www.nowcoder.com/practice/12e081cd10ee4794a2bd70c7d68f5507?tpId=37&tqId=21308&tPage=5&rp=&ru=%2Fta%2Fhuawei&qru=%2Fta%2Fhuawei%2Fquestion-ranking

-------------本文结束感谢您的阅读-------------
Donate comment here