模板來源於博客:https://bestsort.cn/2019/04/28/402/
文本串中模板串的總數
int tot=0;//編號
int trie[N][26];//字典樹
int val[N];//字符串結尾標記
int fail[N];//失配指針
void insert(char * s){//插入模式串
int root=0;//字典樹上當前匹配到的結點
for(int i=0;s[i];i++){
int id=s[i]-'a';//子節點編號
if(trie[root][id]==0)//若之前沒有從root到id的前綴
trie[root][id]=++tot;//插入
root=trie[root][id];//順着字典樹往下走
}
val[root]++;
}
void build(){//構建fail指針域建立字典圖
memset(fail,0,sizeof(fail));
queue<int>q;
for(int i=0;i<26;i++)//將根節點的子節點入隊
if(trie[0][i])
q.push(trie[0][i]);
while(!q.empty()){
int k=q.front();//對於隊首節點k,其fail指針已求得,現在要求的是他子節點的fail指針
q.pop();
for(int i=0;i<26;i++){//遍歷字符集
if(trie[k][i]){//若字符i對應的子節點存在
fail[trie[k][i]]=trie[fail[k]][i];//將這個子節點fail指針賦給fail[k]的字符i對應的節點
q.push(trie[k][i]);
}
else
trie[k][i]=trie[fail[k]][i];//將fail[k]的子節點直接賦成k的子節點
}
}
}
int query(char *t){//對文本串進行匹配
int res=0;//存儲結果
int root=0;//字典樹上當前匹配到的結點
for(int i=0;t[i];i++){//對文本串進行遍歷
int id=t[i]-'a';//子節點編號
root=trie[root][id];//在字典圖中不斷穿梭跳動
int j=root;
while(j&&val[j]!=-1){//利用fail指針找出所有匹配的模式串
res+=val[j];//累加到答案中
val[j]=-1;
j=fail[j];//fail指針跳轉
}
}
return res;
}
char P[N];
char T[N];
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(trie,0,sizeof(trie));
memset(val,0,sizeof(val));
memset(fail,0,sizeof(fail));
tot=0;
int n;//模式串個數
scanf("%d",&n);
while(n--){
scanf("%s",P);//輸入模式串
insert(P);//插入字典樹中
}
build();//構建失配指針與字典圖
scanf("%s",T);//輸入文本串
int res=query(T);
printf("%d\n",res);
}
return 0;
}
文本串中單個模板串的個數
int res[N];
struct AC_Automata{
int tire[N][26];//字典樹
int val[N];//字符串結尾標記
int fail[N];//失配指針
int last[N];//last[i]=j表j節點表示的單詞是i節點單詞的後綴,且j節點是單詞節點
int tot;//編號
void init(){//初始化0號點
tot=1;
val[0]=0;
last[0]=0;
fail[0]=0;
memset(tire[0],0,sizeof(tire[0]));
}
void insert(char *s,int v){//構造trie與val數組,v需非0,表示一個單詞節點
int len=strlen(s);
int root=0;
for(int i=0;i<len;i++){
int id=s[i]-'a';
if(tire[root][id]==0){
tire[root][id]=tot;
memset(tire[tot],0,sizeof(tire[tot]));
val[tot++]=0;
}
root=tire[root][id];
}
val[root]=v;
}
void build(){//構造fail與last
queue<int> q;
for(int i=0;i<26;i++){
int root=tire[0][i];
if(root!=0){
fail[root]=0;
last[root]=0;
q.push(root);
}
}
while(!q.empty()){//bfs求fail
int k=q.front();
q.pop();
for(int i=0;i<26; i++){
int u=tire[k][i];
if(u==0)
continue;
q.push(u);
int v=fail[k];
while(v && tire[v][i]==0)
v=fail[v];
fail[u]=tire[v][i];
last[u]=val[fail[u]]?fail[u]:last[fail[u]];
}
}
}
void print(int i){//遞歸打印與結點i後綴相同的前綴節點編號
if(val[i]){
res[val[i]]++;
print(last[i]);
}
}
void query(char *s){//匹配
int len=strlen(s);
int j=0;
for(int i=0;i<len;i++){
int id=s[i]-'a';
while(j && tire[j][id]==0)
j=fail[j];
j=tire[j][id];
if(val[j])
print(j);
else if(last[j])
print(last[j]);
}
}
}ac;
char P[1000][1000];
char T[N];
int main(){
int n;
while(scanf("%d",&n)!=EOF&&n){
memset(res,0,sizeof(res));
ac.init();
for(int i=1;i<=n;i++){
scanf("%s",P[i]);
ac.insert(P[i],i);
}
ac.build();
scanf("%s",T);
ac.query(T);
for(int i=1;i<=n;i++)
if(res[i])
printf("%s: %d\n",P[i],res[i]);
}
return 0;
}