HDU2222( AC自动机两种模板)

题目链接

题意就是给n 个单词,然后给你一个文本串。问在这个文本串中出现这n个单词的数量。

用一个val[i]保存i节点结尾的单词个数就可以了。

两种模板:

第一种来自我之前的博客:博客

#include
using namespace std;
const int M=60,N=1e6+10;
char s[N];
struct ac_auto
{
    int ne[N][26],val[N],fail[N],sz;
    void init()
    {
        memset(ne[0],0,sizeof(ne[0]));
        sz=0;
    }
    void insert(char *s)//字典树部分
    {
        int o=0;
        for(int i=0;s[i];++i)
        {
            int c=s[i]-'a';
            if(ne[o][c]==0){
                ne[o][c]=++sz;
                val[sz]=0;
                memset(ne[sz],0,sizeof(ne[sz]));
            }
            o=ne[o][c];
        }
        val[o]++;
    }
 
    void build()//bfs处理fail指针,指向深度小于自己的字符相同 最近的那个节点
    {
        queueque;
        for(int i=0;i<26;++i)
        if(ne[0][i]){
            que.push(ne[0][i]);
            fail[ne[0][i]]=0;//第一层节点的fail指针指向根节点
        }
        while(que.size())
        {
            int o=que.front();que.pop();
            for(int i=0;i<26;++i){
                if(ne[o][i])
                {
                    int v=ne[o][i];
                    int fa=fail[o];
                    while(fa&&!ne[fa][i]) fa=fail[fa];//如果当前节点没有 i 号儿子,继续跳fail指针,直到根或者有 i 号节点
                    fail[v]=ne[fa][i];
                    que.push(v);
                }
            }
        }
    }
 
    int query(char *s)//查询  类字典树查询套 跳fail指针 保存答案
    {
        int o=0,ans=0;
        for(int i=0;s[i];++i)
        {
            int c=s[i]-'a';
            while(!ne[o][c]&&o) o=fail[o];
            o=ne[o][c];
            int tmp=o;
            while(tmp)
            {
                ans+=val[tmp];
                val[tmp]=0;
                tmp=fail[tmp];
            }
        }
        return ans;
    }
}ac;
int main()
{
    int _;cin>>_;while(_--)
    {
        int n;
        scanf("%d",&n);
        ac.init();
        for(int i=1;i<=n;++i){
            scanf("%s",s);
            ac.insert(s);
        }
        ac.build();
        scanf("%s",s);
        printf("%d\n",ac.query(s));
    }
}

 

第二种模板:

#include
using namespace std;
const int N=1e6+10;
int n, m;
char s[N];
struct node
{
    int tr[N][26], fail[N], num[N], cnt = 0;
    void init(){
        memset(tr[0], 0, sizeof(tr[0])), cnt=0;
    }

    void insert(char s[]){
        int n = strlen(s + 1), root = 0;
        for(int i = 1; i <= n; ++i){
            if(!tr[root][s[i] - 'a']) {
                tr[root][s[i] - 'a'] = ++cnt;
                memset(tr[cnt], 0, sizeof(tr[cnt])), num[cnt] = 0;
            }
            root = tr[root][s[i]-'a'];
        }
        //printf("cnt:%d root:%d\n", cnt, root);
        num[root]++;
    }

    void getfail(){
        queue que;
        for(int i = 0; i < 26; ++i){
            if(tr[0][i]) que.push(tr[0][i]), fail[tr[0][i]] = 0;
        }

        while(que.size()){
            int now = que.front(); que.pop();
            for(int i = 0; i < 26; ++i){
                if(tr[now][i]) fail[tr[now][i]] = tr[fail[now]][i], que.push(tr[now][i]);
                else tr[now][i] = tr[fail[now]][i];
            }
        }
    }
    int find(char s[]){
        int ans = 0;
        int root = 0, len = strlen(s+1);
        for(int i = 1; i <= len; ++i){
            root = tr[root][s[i] - 'a'];
            int tmp = root;
            while(tmp){
                //printf("tmp:%d\n",tmp);
                ans += num[tmp];
                num[tmp] = 0;
                tmp = fail[tmp];
            }
        }
        return ans;
    }
}ac;
int main()
{
    int _;scanf("%d", &_);while(_--){
        ac.init();
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i){
            scanf("%s", s + 1);
            ac.insert(s);
        }
        ac.getfail();
        scanf("%s", s+1);
        printf("%d\n", ac.find(s));
    }
    return 0;
}

两种模板的区别 在于构建fail指针的时候不同

相比来说第二种会比第一种快一点(个人理解),因为构建fail指针的时候  中间没有反复跳fail

你可能感兴趣的:(字符串---AC自动机)