对于所有模板串建立广义\(SAM\),插入第\(i\)个串的过程中给经过的点打上第\(i\)种标记;然后将每一个询问串放在\(SAM\)上跑一边得到该串对应在\(SAM\)的状态,如果为空答案就是\(0\),否则我们需要求这个状态的标记数量。
因为如果某个串被打了某些标记,那么它在\(parent\)树上的祖先也要有这些标记。所以问题等价于:某个点在\(parent\)树上的子树中有多少种标记,放到\(parent\)树的\(dfs\)序上就是HH的项链。将询问离线,跑出dfs序然后树状数组解决。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
#include<cstring>
#include<algorithm>
//This code is written by Itst
using namespace std;
int N , M;
const int MAXN = 2e5 + 7;
namespace SAM{
int Lst[MAXN] , Sst[MAXN] , fa[MAXN] , trans[MAXN][26];
int cnt = 1 , L;
char s[MAXN];
vector < int > col[MAXN];
int insert(int p , int len , int x){
int t = ++cnt;
Lst[t] = len;
while(p && !trans[p][x]){
trans[p][x] = t;
p = fa[p];
}
if(!p){
Sst[t] = fa[t] = 1;
return t;
}
int q = trans[p][x];
Sst[t] = Lst[p] + 2;
if(Lst[q] == Lst[p] + 1){
fa[t] = q;
return t;
}
int k = ++cnt;
memcpy(trans[k] , trans[q] , sizeof(trans[k]));
Lst[k] = Lst[p] + 1;
Sst[k] = Sst[q];
Sst[q] = Lst[p] + 2;
fa[k] = fa[q];
fa[q] = fa[t] = k;
while(trans[p][x] == q){
trans[p][x] = k;
p = fa[p];
}
return t;
}
vector < int > ch[MAXN];
int dfn[MAXN] , sz[MAXN] , ind[MAXN] , ts;
void dfs(int x){
dfn[x] = ++ts;
ind[ts] = x;
sz[x] = 1;
for(int i = 0 ; i < ch[x].size() ; ++i){
dfs(ch[x][i]);
sz[x] += sz[ch[x][i]];
}
}
void init(){
for(int i = 2 ; i <= cnt ; ++i)
ch[fa[i]].push_back(i);
dfs(1);
}
#define PII pair < int , int >
#define st first
#define nd second
#define PIII pair < PII , int >
#define lowbit(i) ((i) & -(i))
int ans[MAXN] , BIT[MAXN] , bef[MAXN];
vector < PIII > query;
PII go(){
scanf("%s" , s + 1);
int L = strlen(s + 1) , cur = 1;
for(int i = 1 ; i <= L ; ++i)
if(trans[cur][s[i] - 'a'])
cur = trans[cur][s[i] - 'a'];
else
return PII(0 , 0);
return PII(sz[cur] + dfn[cur] - 1 , dfn[cur]);
}
void modify(int x , int num){
if(!x) return;
while(x <= cnt){
BIT[x] += num;
x += lowbit(x);
}
}
int get(int x){
int sum = 0;
while(x){
sum += BIT[x];
x -= lowbit(x);
}
return sum;
}
void work(){
for(int i = 1 ; i <= M ; ++i){
PII t = go();
if(t.st)
query.push_back(PIII(t , i));
}
sort(query.begin() , query.end());
int cur = 0;
for(int i = 0 ; i < query.size() ; ++i){
while(cur < query[i].st.st){
++cur;
for(int j = 0 ; j < col[ind[cur]].size() ; ++j){
modify(bef[col[ind[cur]][j]] , -1);
modify(bef[col[ind[cur]][j]] = cur , 1);
}
}
ans[query[i].nd] = get(query[i].st.st) - get(query[i].st.nd - 1);
}
for(int i = 1 ; i <= M ; ++i)
cout << ans[i] << endl;
}
}
namespace Trie{
int ch[MAXN][26] , dep[MAXN] , ind[MAXN] , cnt = 1;
char s[MAXN];
vector < int > col[MAXN];
void insert(int c){
scanf("%s" , s + 1);
int L = strlen(s + 1) , cur = 1;
for(int i = 1 ; i <= L ; ++i){
if(!ch[cur][s[i] - 'a']){
ch[cur][s[i] - 'a'] = ++cnt;
dep[cnt] = dep[cur] + 1;
}
cur = ch[cur][s[i] - 'a'];
col[cur].push_back(c);
}
}
void build(){
queue < int > q;
q.push(ind[1] = 1);
while(!q.empty()){
int t = q.front();
q.pop();
for(int i = 0 ; i < 26 ; ++i)
if(ch[t][i]){
ind[ch[t][i]] = SAM::insert(ind[t] , dep[ch[t][i]] , i);
SAM::col[ind[ch[t][i]]] = col[ch[t][i]];
q.push(ch[t][i]);
}
}
SAM::init();
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in" , "r" , stdin);
//freopen("out" , "w" , stdout);
#endif
cin >> N >> M;
for(int i = 1 ; i <= N ; ++i)
Trie::insert(i);
Trie::build();
SAM::work();
return 0;
}