【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)$的...

你可能感兴趣的:(poj)