[BZOJ2341][Shoi2011]双倍回文 manacher+std::set

题目链接

发现双倍回文串一定是中心是#的回文串。

所以考虑枚举#点。发现以(i)为中心的双倍回文的左半部分是个回文串,其中心一定位于(i-frac{pal[i]-1}2)(i-1)之间,而且越远越好。所以我们用一个(set)来存一下目前为止回文右端点(geq i)的点,然后在(set)中找到大于等于(i-frac{pal[i]-1}2)的最小值即可。

#include<bits/stdc++.h>
using namespace std;
#define fec(i,x,y) (int i=head[x],y=g[i].to;i;i=g[i].ne,y=g[i].to)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define isin(x,S) (((S)>>((x)-1))&1)
#define fi first
#define se second
#define pb push_back
template<typename I>inline void read(I&x){int f=0,c;while(!isdigit(c=getchar()))c=='-'?f=1:0;x=c&15;while(isdigit(c=getchar()))x=(x<<1)+(x<<3)+(c&15);f?x=-x:0;}
template<typename A,typename B>inline char SMAX(A&a,const B&b){return a<b?a=b,1:0;}
template<typename A,typename B>inline char SMIN(A&a,const B&b){return a>b?a=b,1:0;}
typedef long long ll;typedef unsigned long long ull;typedef pair<int,int>pii;

const int N=500000+7;
int n,pal[N<<1],ans;char a[N],s[N<<1];

inline void Manacher(){
	int p=0,pos=0;
	for(int i=1;i<=n;++i){
		if(p>i)pal[i]=min(p-i+1,pal[(pos<<1)-i]);else pal[i]=1;
		while(s[i+pal[i]]==s[i-pal[i]])++pal[i];
		if(SMAX(p,i+pal[i]-1))pos=i;
	}
}

set<int>t;
vector<int>p[N<<1];
int main(){
	#ifdef hzhkk
	freopen("hkk.in","r",stdin);
	#endif
	scanf("%d%s",&n,a+1);
	s[0]=s[1]='#';
	for(int i=1;i<=n;++i)s[i<<1]=a[i],s[i<<1|1]='#';
	n=n<<1|1;
	Manacher();
	for(int i=1;i<=n;i+=2){
		t.insert(i);
		if(i+pal[i]-1<=n)p[i+pal[i]-1].pb(i);
		if(i&1)SMAX(ans,(i-*t.lower_bound(i-(pal[i]-1)/2))*2);
		for(vector<int>::iterator j=p[i].begin();j!=p[i].end();++j)t.erase(*j);
		for(int i=1;i<=n;++i)
	}
	printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/hankeke/p/BZOJ2342.html