题意:
给你串s0和t,t的长度为n。si的构造方法如下:
s
i
=
s
i
−
1
t
[
i
]
s
i
−
1
s_i=s_{i-1}t[i]s_{i-1}
si=si−1t[i]si−1
有q个询问,问你
s
k
s_k
sk中有多少个w
题解:
首先这个构造方法就能看出来不能直接暴力地去做。那么我们找到第一个
s
i
s_i
si使得
∣
s
i
∣
>
∣
w
∣
|s_i|>|w|
∣si∣>∣w∣。可以发现,si在之后出现的次数每次都是*2.那么我们只需要找到si中有多少个w即可。那么对于i+1~n的情况该怎么做,
我们可以发现,
s
j
=
s
j
−
1
C
1
s
j
−
1
,
s
k
=
s
k
−
1
C
2
s
k
−
1
,
s_j=s_{j-1}C_1s_{j-1},s_k=s_{k-1}C_2s_{k-1},
sj=sj−1C1sj−1,sk=sk−1C2sk−1,在
j
,
k
>
i
,
C
1
=
=
C
2
j,k>i,C_1==C_2
j,k>i,C1==C2的时候,串w的出现次数增加数量是相同的,
num[i][j]表示字符i到了第j个串的时候出现的次数,这样预处理出来所有的情况数。
然后我们对于w找到每个位置是否能作为t串增加的那一个字符,也就是处理出每个位置是否能作为si的前后缀。用两次kmp即可
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
const int N=2e6+5;
vector<string>tmp;
string s,t,ss;
int n,q;
ll num[26][N/20+5],p[N/20+5];
void init(){
int len=s.length(),now=0;
ss=s;
while(len<=N&&now<=n){
tmp.push_back(ss);
ss=ss+t[now++]+ss;
len=len*2+1;
}
for(int i=0;i<26;i++){
for(int j=1;j<=n;j++)
num[i][j]=(num[i][j-1]*2+(t[j-1]-'a'==i))%mod;
}
p[0]=1;
for(int i=1;i<=n;i++)p[i]=p[i-1]*2%mod;
}
int f[N*2];
void kmp(string &st){
f[0]=-1,f[1]=0;
int len=st.length();
int i=1,j=0;
while(i<len&&j<len){
if(j==-1||st[i]==st[j])f[++i]=++j;
else j=f[j];
}
}
string w,pre,suf;
bool can[2][N/2+5];
void cal(int id,int now){
int len=w.length();
memset(can[id],0,sizeof(bool)*(len+1));
while(now>=0)can[id][now]=1,now=f[now];
if(now>=0)can[id][now]=1;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>q>>s>>t;
init();
while(q--){
int k;
cin>>k>>w;
int now=0,wlen=w.length();
while(now<k&&tmp[now].size()<=wlen)now++;
if(tmp[now].size()<wlen){
printf("0\n");
continue;
}
ll ans=0;
pre=w+"#"+tmp[now];
kmp(pre);
int len=pre.length();
cal(0,f[len]);
for(int i=wlen;i<=len;i++)
ans+=f[i]==wlen;
ans=ans*p[k-now]%mod;
suf=tmp[now]+"#"+w;
kmp(suf);
cal(1,f[len]);
for(int i=0;i<wlen;i++){
if(!can[0][i]||!can[1][wlen-i-1])continue;
int v=w[i]-'a';
ans=(ans+num[v][k]-num[v][now]*p[k-now])%mod;
if(ans<0)ans+=mod;
}
printf("%lld\n",ans);
}
return 0;
}