题解:挺好的一道题,第一遍记不清多久写的了不过肯定是抄的。 考虑暴力,枚举匹配位置,尽量匹配一个前缀和后缀,如果中间空缺的位置长度
≥
k
\ge k
≥k 就有 1 的贡献 我们对询问串极其反串建
A
C
AC
AC 自动机,考虑对于正串一个结点,假设它代表的串的长度为
i
i
i,钦定
S
S
S 与这个正串的前
i
i
i 位完美匹配,那么现在需要保证后
i
+
k
+
1
i+k+1
i+k+1 位要完美匹配。设当前节点为
u
u
u,反过来的
A
C
AC
AC 自动机的
i
+
k
+
1
i+k+1
i+k+1 代表的节点为
v
v
v,那么需要合法的匹配位置就是正着的第
j
j
j 位出现在
u
u
u 的
f
a
i
l
fail
fail 子树中,反过来的第
j
+
k
+
1
j+k+1
j+k+1 位出现在
v
v
v 的子树中,因为要保证
i
+
k
+
1
i+k+1
i+k+1 这个后缀是
j
+
k
+
1
j+k+1
j+k+1 这个后缀的前缀。
所以问题被我们简化了,对于正着的第
j
j
j 位走到的节点,我们把反过来的
A
C
AC
AC 自动机的
j
+
k
+
1
j+k+1
j+k+1 代表的节点点亮,如果我们将所有
u
u
u 子树中的对应的节点点亮后,
v
v
v 中点亮的个数就是匹配次数,
d
f
s
dfs
dfs序 +
b
i
t
bit
bit
但是有个小问题,令一个位置匹配尽量匹配前缀后缀后中间的长度为空缺长度
L
L
L,那么一个
L
≤
k
L\le k
L≤k 会被算
k
−
L
+
1
k-L+1
k−L+1 次,我们想让其只算一次,所以我们求出
k
−
1
k-1
k−1 的答案容斥掉。 但这样又会有一个头疼的问题,令前缀匹配长度为
i
i
i,那么次数实际上是
m
i
n
(
i
+
1
,
k
−
L
+
1
)
min(i+1,k-L+1)
min(i+1,k−L+1),将
k
k
k 置为
k
−
1
k-1
k−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';}