SD2018集训Day2 子串
给定一个长度为 (m) 的串 (S),其中每个求有多少长度为 (n) 的子串 (t),满足对其作用一次排列 (p) 之后 不变,即 (t_i = t_{p_i})。 (S) 的字符集大小是 (c),每个字符都是一个 (1) 到 (c) 中的数字。(n,m,cleq 5e5)
这题是个NTT算hash,先算出每个长度为 (n) 的子串的hash值,直接把base的幂数组倒过来和字符串卷即可。(差一定倒过来卷)
然后按排列的顺序把幂数组循环移位,再卷一下判断和原来每个串hash是否相等。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6+200;
int n,m,c;
int p[N],S[N],f[N];
int p1[N],p2[N];
const int mod = 998244353;
const int bas = 1145141;
int lim,l,r[N*4];
int omg[N][2];
#define FOR(i,a,b) for(int i=a;i<=b;++i)
int ksm(int a,int b){
int res=1;
while(b){
if(b&1) res=1ll*res*a%mod;
a=1ll*a*a%mod;b>>=1;
}
return res;
}
void init(){
omg[0][1]=1;omg[0][0]=1;
omg[1][1]=ksm(3,(mod-1)/lim);
omg[1][0]=ksm(omg[1][1],mod-2);
FOR(i,2,lim-1){
omg[i][0]=1ll*omg[i-1][0]*omg[1][0]%mod;
omg[i][1]=1ll*omg[i-1][1]*omg[1][1]%mod;
}
FOR(i,1,lim-1){
r[i]=(r[i>>1]>>1)|((i&1)?(1<<(l-1)):0);
}
}
void ntt(int *a,int opt){
FOR(i,0,lim-1) if(i<r[i]) swap(a[i],a[r[i]]);
for(register int l=2;l<=lim;l<<=1){
int m=l/2;
for(int *g=a;g!=a+lim;g+=l){
for(register int i=0;i<m;++i){
int t=1ll*g[i+m]*omg[lim/l*i][opt]%mod;
g[i+m]=(g[i]-t+mod)%mod;
g[i]=(g[i]+t)%mod;
}
}
}
if(!opt){
int iv=ksm(lim,mod-2);
FOR(i,0,lim) a[i]=1ll*a[i]*iv%mod;
}
}
int main(){
freopen("sub.in", "r", stdin);
freopen("sub.out", "w", stdout);
scanf("%d %d %d",&n,&m,&c);
FOR(i,0,n-1){
scanf("%d",&p[i]);p[i]--;
}
FOR(i,0,m-1){
scanf("%d",&S[i]);
}
f[0]=1;FOR(i,1,n-1) f[i]=1ll*f[i-1]*bas%mod;
FOR(i,0,n-1) p1[n-1-i]=f[i];
FOR(i,0,n-1) p2[n-1-p[i]]=f[i];
lim=1,l=0;while(lim<2*m) lim<<=1,l++;
init();
ntt(p1,1);ntt(p2,1);ntt(S,1);
FOR(i,1,lim) p1[i]=(1ll*p1[i]*S[i])%mod,p2[i]=(1ll*p2[i]*S[i])%mod;
ntt(p1,0);ntt(p2,0);
FOR(i,0,m-n){
if(p1[i+n-1]==p2[i+n-1]){
printf("1");
}else printf("0");
}
return 0;
}