BZOJ3160 万径人踪灭

自古美人都是妖i 提交于 2019-12-05 10:43:06

给定一个长度为 $n$ 的 01 串 $s$,求它的非连续回文子串的数量。

题目链接


首先考虑如何求连续回文子串的数量。

一种可行的方法是使用 Manacher 算法,它设计的初衷是用于求最长回文子串,但在实现的过程中,它求出了一个数组 $R_i$ 代表将原串每两个相邻的字符中间插入分隔符 ‘#’ 之后,以位置 $i$ 为对称轴的最长对称长度,这就意味着以 $i$ 为对称轴有一共有 $leftlfloorfrac {R_i}2 rightrfloor$ 个回文串。这一步的时间复杂度为 $O(N)$.

接下来考虑如何求回文子串的数量。

假定有一个回文子串对称轴为 $i$, 那么这个子串上的任何两个对应的字符 $u,v$ 的下标 $u_p,v_p$ 一定满足 $u_p+v_p=2i$. 如果我们能够求得一个数组 $f$, 其中 $f_i$ 代表有多少对字符关于 $i$ 对称,那么 $i$ 这个对称轴对答案的贡献就是 $2^{f_i}-1$, 因为每对字符都有选和不选两种选择,最终减去空串。

我们在求多项式的卷积的时候,公式为 $C_i=sum_{j+k=i}A_jB_k$, 与此处的情形非常相似,我们处理一个数组 $a$, 其中 $a_i=[s_i=a]$. 然后求 $a$ 和自己的卷积,然后再处理一个数组 $b$, 其中 $b_i=[s_i=b]$. 然后求 $b$ 和自己的卷积。将两个多项式相加,取系数作为 $c$ 数组,那么 $f_i=leftlfloorfrac {c_i+1}2rightrfloor$. 这是因为,$a$ 中的每一位 $i$ 都是当两个位置相加得 $i$, 而且当中的数乘积为 1 (都代表 a) 才会产生 1 的贡献,$b$ 同理。这一步的时间复杂度为 $O(Nlog N)$.


代码如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
struct {	double r,i;	Complex(){ r = i = 0; }	Complex(const double& ir,const double& ii) : r(ir),i(ii) {}	Complex operator + (const Complex& a) const { return Complex(r+a.r,i+a.i); }	Complex operator - (const Complex& a) const { return Complex(r-a.r,i-a.i); }	Complex operator * (const Complex& a) const { return Complex(r*a.r-i*a.i, r*a.i+i*a.r); }	Complex& operator += (const Complex& a)  { return *this = *this+a; }	Complex& operator -= (const Complex& a)  { return *this = *this-a; }	Complex& operator *= (const Complex& a)  { return *this = *this*a; }};const int MAXN = 600000;const long long MOD = 1000000007LL;const double Pi = acos(-1.0);long long Pow(long long a,long long b){	long long ans = 1;	for(;b;b>>=1,(a*=a)%=MOD)		if(b&1) (ans *= a) %= MOD;	return ans;}char s[MAXN];long long ans;int str[MAXN],R[MAXN];long long Manacher(int len){	for(int i=0;i<len;i++){ str[(i<<1)|1] = s[i]; str[i<<1] = '#'; }	len <<= 1; str[len] = '#';	int maxPos = -1,pos; long long ret = 0; 	for(int i=0;i<len;i++){		if(i < maxPos) R[i] = std::min(maxPos-i, R[(pos<<1)-i]); 		else R[i] = 1;		while(i-R[i] >= 0 && i+R[i] <= len && str[i-R[i]]==str[i+R[i]]) ++R[i];		if(i+R[i] > maxPos){ pos = i; maxPos = i+R[i]; }		(ret += R[i]>>1) %= MOD;	} return ret;}Complex arr[MAXN]; int r[MAXN];void FFT(const int& totLevel,const double& type){	int totNums = 1<<totLevel;	for(int i=0;i<totNums;i++) 		if(i<r[i]) std::swap(arr[i],arr[r[i]]);	Complex temp;	for(int nowLevel=0;nowLevel<totLevel;++nowLevel){		const Complex Omega_n = Complex(cos(Pi/(1<<nowLevel)),type*sin(Pi/(1<<nowLevel)));		for(int nowPos=0;nowPos<totNums;nowPos+=(1<<nowLevel+1)){			Complex Omega = Complex(1,0);			for(int i=0;i<(1<<nowLevel);i++){				temp = arr[nowPos+i+(1<<nowLevel)]*Omega;				arr[nowPos+i+(1<<nowLevel)] = arr[nowPos+i] - temp;				arr[nowPos+i] += temp;				Omega *= Omega_n;			}		}	}}long long cnt[MAXN];signed main(void){	int totLen,totLevel,totNums;	scanf("%s", s); totLen = strlen(s);	ans -= Manacher(totLen);		totLevel = 0; totNums = 1;	while(totNums <= (totLen-1<<1)){ totNums <<= 1; ++totLevel; }	for(int i=0;i<totNums;i++) r[i] = (r[i>>1]>>1)|((i&1)<<totLevel-1);	for(int i=0;i<totLen;i++) arr[i].r = s[i]=='a' ? 1 : 0;	FFT(totLevel,1);	for(int i=0;i<totNums;i++) arr[i] *= arr[i];	FFT(totLevel,-1);	for(int i=0;i<totNums;i++) cnt[i] = (int)(arr[i].r/totNums+0.5)+1>>1;    for(int i=0;i<=totNums;i++) arr[i] = Complex(0,0);	for(int i=0;i<totLen;i++) arr[i].r = s[i]=='a' ? 0 : 1;	FFT(totLevel,1);	for(int i=0;i<totNums;i++) arr[i] *= arr[i];	FFT(totLevel,-1);	for(int i=0;i<totNums;i++) cnt[i] += (int)(arr[i].r/totNums+0.5)+1>>1;	//for(int i=0;i<totNums;i++) cnt[i] = (cnt[i]+1)>>1;	for(int i=0;i<totNums;i++) (ans += Pow(2,cnt[i])-1) %= MOD;	printf("%lldn", (ans+MOD)%MOD);	return 0;}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!