给定一个长度为 $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;} |