1. 程式人生 > >Newcoder 39 C.迴文串的交集(Manacher+組合數學)

Newcoder 39 C.迴文串的交集(Manacher+組合數學)

Description

給一個長為 n n 的只含小寫字母的字串

設總共有$ x$ 個迴文連續子串

在這$ x$ 個子串中任選不同的兩個,有公共部分的方案數

答案對 1000000007 1000000007 取模

Input

第一行一個正整數 n n

第二行一個長為 n n 的字串

( n

2 1 0 6 ) (n\le 2\cdot 10^6)

Output

輸出一個整數表示答案

Sample Input

4
babb

Sample Output

6

Solution

假設共有 x x 個迴文串,選出一對方案數為 C x 2 C_x^2 ,考慮選出一對不相交的方案數,對於所有以第 i i 個位置結尾的迴文串,即為 L i L_i ,這些迴文串與出現在 [ i + 1 , n ] [i+1,n] 的一個子串中的迴文串均不相交,記為 S i + 1 S_{i+1} ,那麼答案即為 C x 2 i = 1 n 1 L i S i + 1 C_x^2-\sum\limits_{i=1}^{n-1}L_i\cdot S_{i+1} ,故只要求出 L i , S i L_i,S_i 即可,注意到 S i S_i 其實是所有以第 j j 個位置開始的迴文串的個數 R j R_j 的字尾和 ( j i ) (j\ge i) ,故只用考慮求出以第 i i 個位置結束的迴文串個數 L i L_i 和以第 i i 個位置開始的迴文串個數 R i R_i 即可

對整個串跑一邊 M a n a c h e r Manacher ,對於每個極長的迴文串 [ l , r ] [l,r] ,其後半部分每個位置都會對 L L 產生貢獻,前半部分每個位置都會對 R R 產生貢獻,且貢獻都是 1 1 ,故用字首和優化一下即可 O ( n ) O(n) 得到 L , R L,R ,進而得到答案,注意到所有迴文串個數 x = S 1 x=S_1

Code

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=4000005;
#define mod 1000000007
int add(int x,int y)
{
	x+=y;
	if(x>=mod)x-=mod;
	return x;
}
int mul(int x,int y)
{
	ll z=1ll*x*y;
	return z-z/mod*mod;
}
char s[maxn],ss[maxn];
int ma[maxn];
void Manacher(char *s,int len)
{
	int l=0;
	ss[l++]='$';
	ss[l++]='#';
	for(int i=0;i<len;i++)
	{	
		ss[l++]=s[i];
		ss[l++]='#';
	}
	ss[l]=0;
	int mx=0,id=0;
	for(int i=0;i<l;i++)
	{
		ma[i]=mx>i?min(ma[2*id-i],mx-i):1;
		while(ss[i+ma[i]]==ss[i-ma[i]])ma[i]++;
		if(i+ma[i]>mx)
		{
			mx=i+ma[i];
			id=i;
		}
	}
}
int L[maxn],R[maxn];
int ID(int n)
{
	return (n-1)/2;
}
int main()
{
	int n;
	scanf("%d%s",&n,s);
	Manacher(s,n);
	for(int i=2;i<=2*n;i++)
	{
		int len=2*ma[i]-1;
		if(i&1)
		{
			if(len==1)continue;
			int l1=ID(i-len/2+1),r1=ID(i-1),l2=ID(i+1),r2=ID(i+len/2-1);
			R[l1]++,R[r1+1]--,L[l2]++,L[r2+1]--;
		}
		else
		{
			int l1=ID(i-len/2+1),r1=ID(i),l2=ID(i),r2=ID(i+len/2-1);
			R[l1]++,R[r1+1]--,L[l2]++,L[r2+1]--;
		}
	}
	for(int i=1;i<n;i++)L[i]+=L[i-1],R[i]+=R[i-1];
	for(int i=n-2;i>=0;i--)R[i]=add(R[i],R[i+1]);
	int ans=(ll)R[0]*(R[0]-1)/2%mod;
	for(int i=0;i<n-1;i++)ans=add(ans,mod-mul(R[i+1],L[i]));
	printf("%d\n",ans);
	return 0;
}