KMP+manacher:https://www.luogu.com.cn/problem/P6216
对于一对字符串 ((s_1,s_2)),若 (s_1) 的长度为奇数的子串 ((l,r)) 满足 ((l,r)) 是回文的,那么 (s_1) 的“分数”会增加 (s_2) 在 ((l,r)) 中出现的次数。
现在给出一对 ((s_1,s_2),n=|s1|,m=|s2|),请计算出 (s_1) 的“分数”。
答案对 (2 ^ {32}) 取模。
首先因为要求 ((l,r)) 回文,所以可以用 manacher 求出每一个字符的回文半径,然后根据半径确定以它为对称轴的最长回文串 ((l,r))
然后答案要加上 ((l,r),(l+1,r-1),(l+2,r-2),cdots) 中 (s2) 出现次数和
对于字串 ((l,r)),就看区间 ([l,r-m+1]) 中,有几个字符可以作为 (s2) 的开头并与之完全匹配
这一点可以用 kmp 求出,以 (a_i) 表示 (s1_i) 能否作为 (s2) 的开头并和它完全匹配
这里用前缀和,则求每个区间的复杂度是 (O(1)),但区间个数 (O(n)),还要继续优化
表达成式子,方便表示,设 (L=l,R=r-m+1,maxk=lfloorfrac{R-L}{2}
floor):
[sum_{k=0}^{maxk}sum_{i=L+k}^{R-k}a_i
]
[sum_{k=0}^{maxk}sum(R-k)-sum(L+k-1)
]
[sum_{i=R-maxk}^{R}sum(i)-sum_{i=L-1}^{L+maxk-1}sum(i)
]
发现这里的前缀和,也就是 (sum) 还是一个区间求和的形式,所以可以求一个二次前缀和(前缀和的前缀和),记之为 (summ)
所以原来的式子就是:
[summ(R)-summ(R-maxk-1)-summ(L+maxk-1)-summ(L-2)
]
所以总复杂度就是 (O(n)) 了
题目里说 ((l,r)) 长度仅限奇数,所以 manacher 时不用像模板一样在字符间隙填充无意义字符
但一开始没看到这点,等注意到时代码都写的差不多了,懒得改了
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
#define mod 4294967296ll
int fail[3000005];
char s1[3000005],s2[3000005];
int len1,len2;
int summ[3000005];
char s[6000005];
int n;
int len[6000005];
inline void KMP(){
fail[1]=0;
for(reg int j,i=2;i<=len2;i++){
j=fail[i-1];
while(j&&s2[j+1]!=s2[i]) j=fail[j];
if(j) fail[i]=j+1;
else fail[i]=(s2[1]==s2[i]);
}
for(reg int j=0,i=1;i<=len1;i++){
while(s1[i]!=s2[j+1]&&j) j=fail[j];
if(s1[i]==s2[j+1]) j++;
if(j==len2){
j=fail[j];
summ[i-len2+1]=1;
}
}
}
inline LL manacher(){
LL ans=0;
s[0]=1;s[1]=2;n=1;
for(reg int i=1;i<=len1;i++) s[++n]=s1[i],s[++n]=2;
s[n+1]=3;
reg int pos=0,max=0;
for(reg int L,R,i=1;i<=n;i++){
if(pos+max-1<i) len[i]=1;
else len[i]=std::min(len[pos+pos-i],pos+max-i);
while(s[i+len[i]]==s[i-len[i]]) len[i]++;
if(i+len[i]-1>=pos+max-1) pos=i,max=len[i];
L=(i-len[i]+2)>>1;R=((i+len[i]-2)>>1)-len2+1;
// printf("i : %d, id : %d, len[i] : %d, L : %d, R : %d
",i,i>>1,len[i],L,R);
if(s[i]!=2&&R>=L) ans+=summ[R]-summ[R-((R-L)>>1)-1]-summ[L+((R-L)>>1)-1]+summ[std::max(L-2,0)],ans%=mod;
//长度为奇数
// printf("ans : %d
",ans);
}
return ans%mod;
}
int main(){
len1=read();len2=read();
scanf("%s",s1+1);scanf("%s",s2+1);
if(len1<len2) return puts("0"),0;
KMP();
for(reg int i=1;i<=len1;i++) summ[i]+=summ[i-1];
for(reg int i=1;i<=len1;i++) summ[i]+=summ[i-1];
printf("%lld",manacher());
return 0;
}