codeforces 17E

旧城冷巷雨未停 提交于 2020-01-29 06:08:17

题目链接

题意

给定一个长度为n的小写字母串。问你有多少对相交的回文子 串(包含也算相交) 。 输入格式

第一行是字符串长度n(1<=n<=2*10^6),第二行字符串 输出格式

相交的回文子串个数%51123987

Translated by liyifeng

解法

因为首先是回文串,所以考虑manacher,求出每个节点的f[i],然后考虑直接求相交的回文串对数量不是很好求,所以用总对数减去不相交的对数。

考虑计算不相交的对数,我们求出两个数组;l[i],r[i],表示以i为开头的回文串数量,和以i为结尾的回文串数量,然后l[j]r[i][i<j]\sum l[j]*r[i]*[i<j],就是不相交的回文串对数。

如何计算l[],r[]?可以考虑用差分,对于一个回文中心i,(i-f[i],i]这一段都可以作为左端点出现,然后[i,i+f[i])这一段都可以作为右端点出现。

#include<bits/stdc++.h>
using namespace std;
const int mod=51123987;
const int maxn=4e6+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n;
int f[maxn],m,l[maxn],r[maxn];
char s[maxn];
int main(){
	//freopen("17E.in","r",stdin);
	//freopen("17E.out","w",stdout);
	s[0]='$';m=1;s[1]='#';
	n=read();
	for(int i=1;i<=n;i++){
		m++;
		scanf("%c",&s[m]);
		s[++m]='#';
	}
	int p=0,mx=0;
	for(int i=1;i<=m;i++){
		if(p+mx>i){
			f[i]=min(p+mx-i,f[2*p-i]);
		}
		else{
			f[i]=1;
		}
		while(s[i-f[i]]==s[i+f[i]]){
			f[i]++;
		}
		if(i+f[i]>p+mx){
			p=i;mx=f[i];
		}
	}
	int ans=0;
	for(int i=1;i<=m;i++){
		l[i-f[i]+1]++;l[i+1]--;r[i]++;r[i+f[i]]--;
		ans=(ans+f[i]/2)%mod;//这里除2的原因是在manacher中,会在原串中加新字符,这里消除了新字符的贡献
	}
	ans=1ll*ans*(ans-1)/2%mod;
	for(int i=1,s=0;i<=m;i++){//做法是用总数减去不相交的字符串数量
		l[i]+=l[i-1],r[i]+=r[i-1];
		if(i%2==0){ans=(ans-1ll*s*l[i]%mod)%mod;s=(s+r[i])%mod;}
	}
	printf("%d\n",(ans+mod)%mod);
	return 0;
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!