今天也是为了cc,努力奋斗的一天ヾ(≧▽≦*)o
问题描述
暴力解决方案
动态规划解决方案
使用动态规划解决最长回文子串有很多种解法,下面介绍其中最容易理解的一种。其时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
步骤1——数据结构定义
令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]
一定不是回文子串。
步骤2——状态转移方程
- 问题:如果按照i和j从小到大的顺序来枚举子串的两个端点,然后更新dp[i][j],会无法保证dp[i+1][j-1]已经被计算过,从而无法得到正确的dp[i][j]。
代码
#include<cstdio>
#include<cstring>
const int maxn = 1010;
char S[maxn];
int dp[maxn][maxn];
int main(){
gets(S);
int len = strlen(S),ans = 1;
memset(dp,0,sizeof(dp)); //dp数组初始化为0
//边界
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);
return 0;
}
题型训练
后记
还可以使用中心扩展法,这种方法时间复杂度与动态规划相同,但是空间复杂度只为
O
(
1
)
O(1)
O(1);
可以参考这里