问题描述:
给出一个字符串S,求S的最长回文子串的长度。
例:字符串“ASDFGHGFDSB”的最长回文子串为“SDFGHGFDS”,长度为9。
显然暴力解法的复杂度较大,采用动态规划可以更好的解决这类问题。
令dp[i][j]表示S[i]至S[j]所表示的子串是否回文,是则为1,不是则为0.这样可以根据S[i]和S[j]是否相等分为两种情况:
- 如果S[i]==S[j],那么只要S[i+1]至S[j-1]是回文子串,那么S[i]至S[j]也是回文子串;如果S[i+1]至S[j-1]不是回文子串,那么S[i]至S[j]也不是回文子串;
- 如果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值进行计算。这样问题就可以得到解决,代码如下:
#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);
}