LG4287双倍回文(Manacher)

LG4287双倍回文

解题思路

据说本体有很多乱搞方法,但是可以用 \(O(n)\)manacher 解决。

我们按照正常的 manacher 做,我们要验证 \(i\) 这个位置是否可以为右半回文串的中心位置。由于右半回文串长度为偶数,所以中心位置只可能为 # 字符。我们找到位置 \(i\) 关于 \(mid\) 的对称点 \(j\)。如果以 \(i\) 为中心的最长回文串和以 \(j\) 为中心的最长回文串有交集,那么 \([j,i]\) 这一段子串去掉 # 就可以形成一个所求的字符串。

我们注意到,还有一些限制条件,例如 \(mid\)# 字符。所以我们可以考虑只对为 # 的字符跑 manacher

时间复杂度为 \(O(n)\)。实测跑得飞快。

代码

//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;

int read() {
	char ch=getchar();
	int f=1,x=0;
	while(ch<'0'||ch>'9') {
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
char read_char() {
	char ch=getchar();
	while(!isalpha(ch)) {
		ch=getchar();
	}
	return ch;
}

const int MAXN=1e6+10; 

int n,p,mx,m;
int r[MAXN],c[MAXN];

int main() {
	cin>>n;
	
	c[++m]=27;
	for(int i=1;i<=n;i++) {
		c[++m]=read_char()-'a'+1;
		c[++m]=27;
	}
	int mid=0,mx=0,ans=0;
	for(int i=1;i<=m;i+=2) {
		if(i>mx) {
			r[i]=1;
		}
		else {
			r[i]=min(r[mid*2-i],mx-i+1);
		}
		if(i-r[i]+1<=mid&&i<=mx) {
			ans=max(ans,(i-mid)*2);
		} 
		while(i-r[i]>=1&&i+r[i]<=m&&c[i+r[i]]==c[i-r[i]]) {
			r[i]++;
		}
		if(i+r[i]-1>mx) {
			mid=i;
			mx=i+r[i]-1;
		}
	}
	
	cout<<ans<<endl;
	
	return 0;
}

posted @ 2022-04-02 09:50  huayucaiji  阅读(24)  评论(0编辑  收藏  举报