7-64 最长对称子串 (25 分)
对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?
,最长对称子串为s PAT&TAP s
,于是你应该输出11。
输入格式:
输入在一行中给出长度不超过1000的非空字符串。
输出格式:
在一行中输出最长对称子串的长度。
输入样例:
Is PAT&TAP symmetric?
输出样例:
11
以下仅动态规划法:
令 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][j]={dp[i+1][j−1],S[i]==S[j]0,S[i]!=S[j]dp[i][j]={dp[i+1][j−1],S[i]==S[j]0,S[i]!=S[j]
边界:dp[i][i]=1,dp[i][i+1] = (S[i] == S[i+1]) ? 1 : 0。
根据递推写法从边界出发的原理,注意到边界表示的是长度为 1 和 2 的子串,且每次转移时都对子串的长度减了 1,因此不妨考虑按子串的长度和子串的初始位置进行枚举,即第一遍将长度为 3 的子串的 dp 值全部求出,第二遍通过第一遍结果计算出长度为 4 的子串的 dp 值 ……
算法时间复杂度为O(N ^ 2)。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#define maxn 1010
char S[maxn];
int dp[maxn][maxn];
int main() {
gets(S); // 输入整行字符
int len=strlen(S), ans=1; // ans 记录最长回文子串长度
int i, j, L;
// 边界
for(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(L=3; L<=len; ++L) { // 枚举子串长度
for(i=0; i+L-1 < len; ++i) { // 枚举子串的起始节点
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); // 输出
return 0;
}