【POJ】3974 Palindrome

http://poj.org/problem?id=3974

题意:求s的最长回文串。(|s|<=1000000)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;
char s[2000050]; int len[2000050], T;
int main() {
	while(scanf("%s", s+1), strcmp(s+1, "END")!=0) {
		int n=strlen(s+1), ans=1;
		for(int i=n; i; --i) s[i<<1]=s[i], s[i<<1|1]=0;
		n=n<<1|1;
		s[1]=0; s[0]=1; s[n+1]=2;
		int cur=1;
		for(int i=2; i<=n; ++i) {
			int &now=len[i], pos=(cur<<1)-i; now=0;
			now=min(len[pos], cur+len[cur]-i); now=max(0, now);
			while(s[i-now-1]==s[i+now+1]) ++now;
			if(i+now>cur+len[cur]) cur=i;
			ans=max(ans, now);
		}
		printf("Case %d: %d\n", ++T, ans);
	}
	return 0;
}

  

学习了一下manacher= =

其实就是利用回文串的性质= =回文。。然后维护一个单调的东西= =

大概就是先处理一下串,使串变成奇数(即插入特殊字符,而且一样),这样得到的回文串都是奇数(那么半径其实就是实际的长度,即s[i-半径]~s[i+半径]是回文串)

然后从左往右扫,维护一个回文串能到达的最远的位置,中心为cur,能到达的位置为mx=s[cur+len[cur]]。那么如果i在mx前,那么i关于cur的对称点就是i'=cur*2-i。此时由于对称性,可以得到长度至少为len[i]=min(len[i'], mx-i)。然后再继续向两边拓展,且如果位置大于了mx,记得更新。

可以证明这样是均摊$O(n)$的...

posted @ 2015-03-19 14:21  iwtwiioi  阅读(383)  评论(0编辑  收藏  举报