P3808 AC自动机(简单版)

· · 题解

P3808题目

讨论区的解答

代码

#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e4+10,M=1e6+10,L=55;
int ch[N*L][26],cnt[N*L],idx;
int ne[N*L];//每个结点对应的next值 
int q[N*L];//队列 
char w[L],s[M];//w 单词 s 文章 
int T,n;
void init(){//清空 
    memset(ch,0,sizeof(ch));
    memset(cnt,0,sizeof(cnt));
    idx=0;
    memset(ne,0,sizeof(ne));
}
void insert(char w[]){//建树 
    int p=0;//从根开始 
    for(int i=0;w[i];i++){
        int x=w[i]-'a';
        if(!ch[p][x])ch[p][x]=++idx;
        p=ch[p][x];
    }
    cnt[p]++;//以当前结点结束的单词数加一 
}
//void bfs(){//求每个结点的next值 
//  int h=1,t=0;//默认为空
//  //将根节点下面的第一层结点入队  因为这一层结点next值都为0
//  for(int i=0;i<26;i++){
//      if(ch[0][i])q[++t]=ch[0][i];
//  }
//  //广搜计算每一层结点next值
//  while(h<=t){
//      int f=q[h];//上一层父结点编号 
//      for(int i=0;i<26;i++){
//          if(ch[f][i]){
//              int c=ch[f][i];//子结点编号 
//              int j=ne[f];//父结点next值
//              while(j&&!ch[j][i]){
//                  j=ne[j];
//              }
//              if(ch[j][i]){
//                  j=ch[j][i];
//              }
//              ne[c]=j;
//              q[++t]=c;//入队 
//          }
//      }
//      h++;//出队 
//  }
//}
void bfs(){
    int h=1,t=0;//默认为空
    //将根节点下面的第一层结点入队  因为这一层结点next值都为0
    for(int i=0;i<26;i++){
        if(ch[0][i])q[++t]=ch[0][i];
    }
    //广搜计算每一层结点next值
    while(h<=t){
        int f=q[h];//上一层父结点编号 
        for(int i=0;i<26;i++){
            if(ch[f][i]){
                int c=ch[f][i];//子结点编号 
                //父结点next值 
                ne[c]=ch[ne[f]][i];
                q[++t]=c;//入队 
            }
            else{
                //如果结点f不存在子结点i 存储假设存在结点i,其next值是多少 
                ch[f][i]=ch[ne[f]][i];
            }
        }
        h++;//出队 
    }
}
signed main(){
//  ios::sync_with_stdio(0);
//  cin.tie(0),cout.tie(0);
//  scanf("%d",&T);
//  while(T--){
        init();
        scanf("%d",&n);
        while(n--){
            scanf("%s",w);
            insert(w);
        }//建树 
        bfs();//求每个结点的next值 
        scanf("%s",s);
        int res=0;
        for(int i=0,j=0;s[i];i++){//i 遍历s字符串   
                                  //j 字典树从根开始遍历 
//          int x=s[i]-'a';
//          //匹配不上j回跳 
//          while(j&&!ch[j][x])j=ne[j];
//          if(ch[j][x])j=ch[j][x];//能匹配 j跳子结点 
//          int p=j;
//          while(p!=0){
//              res+=cnt[p];
//              cnt[p]=0;
//              p=ne[p];//看以当前单词后缀为前缀单词是否存在 
//          }
            j=ch[j][s[i]-'a'];
            for(int p=j;p&&~cnt[p];p=ne[p]){
                res+=cnt[p];
                cnt[p]=-1;// 标记已访问,避免重复计算
            }
        }
        printf("%d\n",res);
//  }

    return 0;
}

注意不要重复计算,要做标记,不然会TLE #1