題目描述:
題目分析:
相當於可以將 中的一段長度爲 的區間變爲通配符然後進行匹配。
那麼 能和 匹配的條件是存在一個 的前綴 ,使得 與 匹配, 與 匹配。
考慮枚舉 的一個前綴 ,找出它在 中的匹配位置,因爲是多模式串匹配,我們用AC自動機來做。相當於 對應的節點在 的子樹中。
對於找到的匹配位置 ,還要滿足 是 的後綴,這相當於在反串的AC自動機中 對應的節點在 的子樹中。
這相當於是個二維數點問題,但是可以離線查詢,在第一棵樹 dfs 進入和出來時的答案作差就可以得到子樹內在第二棵樹的貢獻。第二棵樹用樹狀數組在dfs序上單點修改區間查詢即可。
可能 在枚舉多個前綴的時候會在同一個位置被匹配多次:
對應的 的區間一定是連續的,兩個相鄰的選 方案重複相當於 分別與 匹配(即一個長度爲 的匹配),去掉即可。
Code:
#include<bits/stdc++.h>
#define maxn 400005
#define maxc 94
using namespace std;
int n,k,m,ans[maxn],pos[maxn];
char s[maxn],t[maxn];
int ch[maxn][maxc],fail[maxn],tot;
struct node{int id,x,y;};
vector<node>Q[maxn];
vector<int>S[maxn],G[maxn];
int in[maxn],out[maxn],tim,a1[maxn],a2[maxn];
void dfs1(int u){in[u]=++tim; for(int v:G[u]) dfs1(v); out[u]=tim;}
void upd(int *a,int i){for(;i<=tim;i+=i&-i) a[i]++;}
int qsum(int *a,int i){int s=0;for(;i>0;i-=i&-i) s+=a[i];return s;}
void dfs2(int u){
for(node q:Q[u]) ans[q.id]-=qsum(a1,out[q.x])-qsum(a1,in[q.x]-1)-(qsum(a2,out[q.y])-qsum(a2,in[q.y]-1));
for(int x:S[u]) upd(a1,in[pos[x+k+1]]),upd(a2,in[pos[x+k]]);
for(int v:G[u]) dfs2(v);
for(node q:Q[u]) ans[q.id]+=qsum(a1,out[q.x])-qsum(a1,in[q.x]-1)-(qsum(a2,out[q.y])-qsum(a2,in[q.y]-1));
}
int main()
{
scanf("%d%s%d",&k,s+1,&n),m=strlen(s+1);
for(int o=1;o<=n;o++){
scanf("%s",t+1); int L=strlen(t+1);
if(L<=k) {ans[o]=m-L+1; continue;}
int r=0,c;
for(int i=1;i<=L;i++,r=ch[r][c])
if(!ch[r][c=t[i]-33]) ch[r][c]=++tot;
r=pos[L+1]=0;
for(int i=L;i>=1;pos[i]=r=ch[r][c],i--)
if(!ch[r][c=t[i]-33]) ch[r][c]=++tot;
r=0;
for(int i=0;i+k<=L;i++,r=ch[r][t[i]-33])
Q[r].push_back((node){o,pos[i+k+1],i?pos[i+k]:maxn-1});
}
static int q[maxn],L,R; q[L=R=1]=0;
while(L<=R){
int r=q[L++],c; if(r) G[fail[r]].push_back(r);
for(int i=0;i<maxc;i++)
if(c=ch[r][i]) fail[c]=r?ch[fail[r]][i]:0,q[++R]=c;
else ch[r][i]=ch[fail[r]][i];
}
dfs1(0); pos[m+1]=0;
for(int i=m,r=0;i>=1;i--) pos[i]=r=ch[r][s[i]-33];
for(int i=0,r=0;i+k<=m;i++,r=ch[r][s[i]-33]) S[r].push_back(i);
dfs2(0);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}