acm-(好题、kmp、思维、字符串)Good Bye 2020 G. Song of the Sirens


传送门
a n s [ i ] ans[i] ans[i]表示 w w w s i s_i si重复的次数,我们要求的其实就是 a n s [ k ] ans[k] ans[k]
g [ i ] g[i] g[i]表示 w w w s i s_i si中的重复次数,但是必须包含 t i − 1 t_{i-1} ti1

于是不难写出 a n s [ i ] = 2 a n s [ i − 1 ] + g [ i ] ans[i]=2ans[i-1]+g[i] ans[i]=2ans[i1]+g[i],假设 s [ c u r ] s[cur] s[cur]是最小的满足 l e n ( s c u r ) ≥ l e n ( w ) len(s_{cur})\ge len(w) len(scur)len(w)的值,那么 a n s [ i ] = 2 k − c u r a n s [ c u r ] + ∑ j = c u r + 1 k 2 k − j g [ j ] ans[i]=2^{k-cur}ans[cur]+\sum_{j=cur+1}^k2^{k-j}g[j] ans[i]=2kcurans[cur]+j=cur+1k2kjg[j] a n s [ c u r ] ans[cur] ans[cur]可以用kmp快速求出,现在考虑 g [ j ] g[j] g[j]怎么求,由于要求必须经过 t j − 1 t_{j-1} tj1这个字符,而 s s s的形式是 s j − 1 t j − 1 s j − 1 s_{j-1}t_{j-1}s_{j-1} sj1tj1sj1,注意到 l e n ( s j ) > l e n ( s c u r ) len(s_j)>len(s_{cur}) len(sj)>len(scur),那么其实只要 t t t字符相同那么 g g g也是相同的,因此可以考虑求出26个字符下的答案,然后利用前缀和求出 ∑ j = c u r + 1 k 2 k − j g [ j ] \sum_{j=cur+1}^k2^{k-j}g[j] j=cur+1k2kjg[j]这一项的答案即可。

#include 
using namespace std;


const int maxn = 2e5+5;
const int MAX = 1e6+16;
const int mod = 1e9+7;

char s[MAX<<2],t[maxn],w[MAX];
int nxt[MAX],pw[maxn],sum[maxn][26];
void init(char *t,int lent){
     
	int j=0;
	for(int i=2;i<=lent;++i){
     
		while(j && t[i]!=t[j+1])j=nxt[j];
		if(t[i]==t[j+1])j++;
		nxt[i]=j;
	}
}
int cal(char *s,int lens,char *t,int lent){
     
	int j=0;
	int ans=0;
	for(int i=1;i<=lens;++i){
     
		while(j && s[i]!=t[j+1])j=nxt[j];
		if(s[i]==t[j+1])j++;
		if(j==lent){
     
			ans++;
			j=nxt[j];
		}
	}
	return ans;
}
int main(){
     
	int n,q;
	pw[0]=1;
	for(int i=1;i<maxn;++i)pw[i]=pw[i-1]*2%mod;
	scanf("%d%d%s%s",&n,&q,s+1,t+1);
	int lens=strlen(s+1),lent=strlen(t+1);
	for(int j=0;j<26;++j)
	for(int i=1;i<=lent;++i){
     
		sum[i][j]=((sum[i-1][j]<<1)+(t[i]==j+'a'))%mod;
	}
	int len=lens,cur=1;
	while(len<(MAX<<1)){
     
		if(cur==lent+1){
     
			break;
		}
		int new_len=len;
		s[++new_len]=t[cur];
		for(int i=1;i<=len;++i){
     
			s[++new_len]=s[i];
		}
		len=new_len;
		cur++;
	}
	while(q--){
     
		int k;
		scanf("%d%s",&k,w+1);
		int lenw=strlen(w+1);
		len=lens,cur=1;
		bool failed=false;
		while(len<lenw){
     
			if(cur==k+1){
     
				failed=true;
				break;
			}
			len=2*len+1;
			cur++;
		}cur--;
		if(failed){
     
			printf("0\n");
			continue;
		}
		init(w,lenw);
		int ans=1ll*pw[k-cur]*cal(s,len,w,lenw)%mod;
		int l=len-lenw+2,r=len+lenw;
		for(int i=0;i<26;++i){
     
			if(sum[k][i]-1ll*sum[cur][i]*pw[k-cur]%mod){
     
				s[len+1]=i+'a';
				ans=(ans+1ll*cal(s+l-1,r-l+1,w,lenw)%mod*((sum[k][i]-1ll*sum[cur][i]*pw[k-cur]%mod)%mod)%mod)%mod;
			}
		}
		s[len+1]=t[cur+1];
		ans=(ans%mod+mod)%mod;
		printf("%d\n",ans);
	}
} 

你可能感兴趣的:(思维游戏,思维,字符串,acm竞赛,算法,kmp)