传送门
- 开题,咦?怎么做过,咦?怎么不会了
 - 题解:挺好的一道题,第一遍记不清多久写的了不过肯定是抄的。
考虑暴力,枚举匹配位置,尽量匹配一个前缀和后缀,如果中间空缺的位置长度就有 1 的贡献
我们对询问串极其反串建自动机,考虑对于正串一个结点,假设它代表的串的长度为
,钦定
与这个正串的前
位完美匹配,那么现在需要保证后
位要完美匹配。设当前节点为
,反过来的
自动机的
代表的节点为
,那么需要合法的匹配位置就是正着的第
位出现在
的
子树中,反过来的第
位出现在
的子树中,因为要保证
这个后缀是
这个后缀的前缀。
 - 所以问题被我们简化了,对于正着的第
位走到的节点,我们把反过来的
自动机的
代表的节点点亮,如果我们将所有
子树中的对应的节点点亮后,
中点亮的个数就是匹配次数,
序 +
 - 但是有个小问题,令一个位置匹配尽量匹配前缀后缀后中间的长度为空缺长度
,那么一个
会被算
次,我们想让其只算一次,所以我们求出
的答案容斥掉。
但这样又会有一个头疼的问题,令前缀匹配长度为,那么次数实际上是
,将
置为
算可能减掉相同的量,这里需要特殊处理。
 
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 4e5 + 50;
int k, n, m; char S[N];
int len[N], ch[N][95], fail[N], as[N], nd;
vector<int> G[N]; int in[N], out[N], sgn;
struct data{ 
  int c, u, v, ur, vr; 
  data(int _c = 0, int _u = 0, int _v = 0, int _ur = 0, int _vr = 0){ 
    c = _c; u = _u; v = _v; ur = _ur; vr = _vr;
  } 
};
vector<data> v[N];
vector<int> p[N],q[N];
void ins(int w, char *S){
  int len = strlen(S+1), now = 0;
  for(int i = 1; i <= len; i++){
    int c = S[i] - 33; if(!ch[now][c]) ch[now][c] = ++nd;
    now = ch[now][c];
  }
  static int ps[N]; ps[len+1] = 0; now = 0;
  for(int i = len; i >= 1; i--){
    int c = S[i] - 33; if(!ch[now][c]) ch[now][c] = ++nd;
    now = ch[now][c]; ps[i] = now;
  }
  for(int i = 0, now = 0; i + k <= len; i++){
    data nx(w, ps[i+k+1], i ? ps[i+k] : -1); 
    v[now].push_back(nx); now = ch[now][S[i+1]-33];
  }
}
void build(){
  queue<int> q;
  for(int i = 0; i <= 94; i++) if(ch[0][i]) q.push(ch[0][i]);
  while(!q.empty()){
    int x = q.front(); q.pop();
    for(int i = 0; i <= 94; i++)
    if(ch[x][i]) fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
    else ch[x][i] = ch[fail[x]][i];
  }
  for(int i = 1; i <= nd; i++) G[fail[i]].push_back(i);
}
void pre_dfs(int u){ in[u] = ++sgn; for(int v : G[u]) pre_dfs(v); out[u] = sgn; }
struct BIT{
  int c[N];
  void add(int x){ for(;x<=sgn;x+=x&-x) ++c[x]; }
  int ask(int x){ int as=0; for(;x;x-=x&-x) as+=c[x]; return as; }
  int qry(int l, int r){ return ask(r)-ask(l-1); }
}T[2];
void work(int u){
  for(auto &t : v[u]){
    t.ur = T[0].qry(in[t.u],out[t.u]);
    if(~t.v) t.vr = T[1].qry(in[t.v],out[t.v]);
  }
  for(auto t : p[u]) T[0].add(in[t]);
  for(auto t : q[u]) T[1].add(in[t]);
  for(int v : G[u]) work(v);
  for(auto t : v[u]){
    as[t.c] += T[0].qry(in[t.u],out[t.u]) - t.ur;
    if(~t.v) as[t.c] -= T[1].qry(in[t.v],out[t.v]) - t.vr;
  }
}
int main(){
  scanf("%d%s%d",&k,S+1,&m); n = strlen(S+1);
  for(int i = 1; i <= m; i++){
    static char s[N];
    scanf("%s",s+1); int len = strlen(s+1);
    if(len <= k){ as[i] = max(0, n-len+1); continue; } ins(i,s);
  } build();
  static int pos[N];
  for(int i = n, now = 0; i >= 1; i--) pos[i] = now = ch[now][S[i]-33];
  for(int i = 0, now = 0; i + k <= n; i++){
    p[now].push_back(pos[i+k+1]);
    q[now].push_back(pos[i+k]); now = ch[now][S[i+1]-33];
  } pre_dfs(0); work(0); 
  for(int i = 1; i <= m; i++) cout << as[i] << '\n';
}                
                










