hdu3608最长回文子串


       首先做这个题目时候以为是动态规划,可惜的是超时了。虽然超时了,但还是先说说动态规划。设原串为s用DP[i,j]表示s[i...j]中最大回文字串的长度,则DP[i,j]的子问题可以划分为DP[i+1,j],DP[i,j-1],以及DP[i+1,j-1].当然还需要一个数组flag[i,j]来记录状态。若flag[i,j]=1,则它表示在 s[i,j]中两个端点字符s[i】与s[j] 是s[i..j]最长回文子串构成的部分,即s[i,j]就是一个回文串。否则flag[i,j]=0表示s[i]和s[j]不同时是s[i..j]子串的构成部分。状态转移方程可以如下:

          若flag[i ][j]==0 or s[i]!=s[j] ,DP[i,j]=max{ DP[i+1,j],DP[i,j-1]},若flag[i][j]==1 and s[i]==s[j] ,DP[i,j]=DP[i-1][j-1]+2.

 具体处理时,不用开一个很大的数组,因为打表时候用到的只有那几行,定义DP[3][MAX_SIZE]即可,计算时采用取模运算,详见代码。很可惜,这是O(n^2)的算法AC不了。

#include<iostream>
#include<string>
using namespace std;
#define MAX_SIZE 110004
#define n 3
int DP[n][MAX_SIZE];
bool flag[n][MAX_SIZE];
char s[MAX_SIZE];
int The_Longest_Palindrome();
int MAX(int a, int b){
	return a > b ? a : b;
}
int main(){
	int ans;
	while (cin>>s){
		ans = The_Longest_Palindrome();
		printf("%d\n", ans);
	}
	return 0;
}
int The_Longest_Palindrome(){
	int l, L, i, j;
	L = strlen(s);
	for (j = 1; j <= L + 1; j++){
		DP[j%n][j] = 1;     //初始化
		DP[j%n][j - 1] = 0;
		flag[j%n][j] = flag[j%n][j-1] = 1;
	}
	DP[0][0] = 1;
	for (l = 2; l <= L; l++){             //长度
		for (i = 0; i <= L - l; i++){
			j = i + l - 1;           //计算出 j 的位置
			if (s[i] == s[j] && flag[(i + 1) % n][j - 1]){
				DP[i%n][j] = 2 + DP[(i + 1) % n][j - 1];
				flag[i%n][j] = 1;
			}
			else{
				DP[i%n][j] = MAX(DP[(i + 1) % n][j], DP[i%n][j - 1]);   //子序列
				flag[i%n][j] = 0;
			}
		}
	}
	return DP[0][L - 1];
}

 下面用到的算法参考博文:Manacher算法总结 - OPEN 开发经验库http://www.open-open.com/lib/view/open1419150233417.html

叫做Manacher算法。

     定义p[i]表示以第i个字符为中心的最大回文串的半径长度(包括第 i 个字符)。计算的时候从前向后扫描一遍,也就是说在计算p[i+k]的时候p[0...i+k-1]都是计算好了的。假设 i+p[i] 是在已经计算完的序列中能延伸到的最右端。我们下一步计算p[i+k],那么点i+k必定包含在p[i]+i中,即为i+k<=i+p[i] => k<=p[i] ,由对称性可知。下面的讨论分为3个情况:

        1)i-k位置的回文串完全包含在i位置的回文串内部,i-k回文串最左端位置为:i-k-p[i-k],而 i 回文串最左端为; i-p[i] 。这时候 i 回文串在i-k回文串左端的左端,即有 i-p[i]<i-k-p[i-k] => p[i]>k+p[i-k]. 对称可知,i+k的回文串至少有 一段和i-k的回文串相同 。倘若i+k的回文串向两边延伸,势必会得到 i-k 的串也会增长,从而i+k的串不可能再向两边延伸。故而有:

          若p[i]>k+p[i-k] 则:

                                      p[i+k]=p[i-k];

         2) i-k位置的回文串左端 恰好与 i 位置的回文串左端重合。此时有i-k-p[i-k]=i-p[i] => p[i]=k+p[i-k].这个时候至少有 p[i+k]=p[i-k],但是此时i+k串向两边延伸是合法的,不会产生矛盾。故而有:

          若p[i]=k+p[i-k]则:

                                      p[i+k]=p[i-k];

                                      while(s[i+k+p[i+k]]==s[i+k-p[i+k]]) p[i+k]++;

         3)i-k位置的回文串最左端超过了 i 位置的回文串,即有 i-k-p[i-k]<i-p[i] =>p[i]<p[i-k]+k .此时由于 i 回文串对称性可知 至少有 i+k的回文串与i-k的回文串在 i 回文串内部的那部分相等,即至少有 p[i+k]=p[i]-k,倘若i+k的回文串可以向两边延伸,那么势必会让 i 的回文串也增长吗,这样与i的回文串矛盾。故而有;

           若 p[i]<k+p[i-k]则:

                                       p[i+k]=p[i]-k;

以上三种情况综合起来考虑就是:

                p[i+k]=min{p[i]-k,p[i-k]} ;

                while(s[i+k+p[i+k]]==s[i+k-p[i+k]])   p[i+k]++;

具体处理的时候将长度为len的串每个字符和两端都插上一个字符‘#’,或者其它串中没有出现的字符。此时串长度变为2*len+1.

#include<iostream>
using namespace std;
char ch[110003 * 2];
int p[110003 * 2];
int Min(int a, int b){
	return a < b ? a : b;
}
int main(){
	int i, len, k, maxlen;
	p[0] = 1, p[1] = 2;
	while (cin>>ch){
		len = strlen(ch);
		for (i = len - 1; i >= 0; i--){
			ch[2 * i + 2] = '#';
			ch[2 * i + 1] = ch[i];
		}
		ch[0] = '#';
		//----------------------------------------完成初始化插值
		len = len * 2 + 1;                       //新数组长度
		i = k = 1;
		maxlen = 2;
		while (i + k < len){
			p[i + k] = Min(p[i - k], p[i] - k);                  //计算p[i+k]
			while (i + k >= p[i + k] && ch[i + k + p[i + k]] == ch[i + k - p[i + k]])
				p[i + k]++;                                   //将p[i+k]向两边延伸
			if (maxlen < p[i + k])
				maxlen = p[i + k];                            //更新maxlen
			if (p[i + k] + i + k >= p[i] + i){                //更新 i,还原k
				i += k;
				k = 1;
			}
			else
				k++;                                          //更新k
		}
		printf("%d\n", maxlen - 1);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值