SD2018集训Day2 子串

SD2018集训Day2 子串

给定一个长度为 \(m\) 的串 \(S\),其中每个求有多少长度为 \(n\) 的子串 \(t\),满足对其作用一次排列 \(p\) 之后 不变,即 \(t_i = t_{p_i}\)\(S\) 的字符集大小是 \(c\),每个字符都是一个 \(1\)\(c\) 中的数字。\(n,m,c\leq 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;
}
posted @ 2021-01-11 20:03  lcyfrog  阅读(72)  评论(0编辑  收藏