ac自动机

文章目录[隐藏]

  • 优化防止卡到

  • 模板

    void insert() //建trie树
    {
        int p = 0;
        for (int i = 0; str[i]; i ++ )
        {
            int t = str[i] - 'a';
            if (!tr[p][t]) tr[p][t] = ++ idx;
            p = tr[p][t];
        }
        cnt[p] ++ ;
    }
    
    void build()
    {
        int hh = 0, tt = -1;
        for (int i = 0; i < 26; i ++ )
            if (tr[0][i])
                q[ ++ tt] = tr[0][i];//加入队列
        while(hh<=tt){
    	int t=q[hh++];
    	for(int i=0;i<26;i++){
    		int c=tr[t][i];//看看她的儿子 
    		if(!c) continue ;//可能没有 
    		int j=next[t];//t这个结点可以向上匹配去到哪个位置 
    		while(j&&!tr[j][i]) j=next[j];//如果j对应的第i个字符不存在说明没有找到相同的后缀 所以要再次跳转 
    		if(tr[j][i]) j=tr[j][i];//最后如果找到了儿子 那么就走过去	 
    		next[c]=j;//让next c等于那个字符  c是idx 
    		q[++t]=c//多加一个idx 
    	}
    }
    
    
    t= str[i]-'a'; 
    for(int i=0,j=0;str[i];i++){
    	int t=str[i]-'a';
    	while(j&&!tr[j][t]) j=ne[j];//不存在这个结点就挑走 
    	if(tr[j][t]) j=tr[j][t];//存在就走过去 找到匹配到最靠下的结点
    	 
    	
    }
    

    优化防止卡到

    void build()
    {
        int hh = 0, tt = -1;
        for (int i = 0; i < 26; i ++ )
            if (tr[0][i])
                q[ ++ tt] = tr[0][i];//加入队列
        while(hh<=tt){
    	int t=q[hh++];
    	for(int i=0;i<26;i++){
    		int p=tr[t][i];//看看她的儿子 
    		if(!c) tr[t][i]=tr[ne[t]][i] ;//如果不存在 直接存下next应该走到的位置 下次不需要在词条 
    		else{
                      ne[p]=tr[ne[t]][i];
                      q[++t]=p;
                      }
                     
                 }
    
    }
    
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 10010, S = 55, M = 1000010;
    
    int n;
    int tr[N * S][26], cnt[N * S], idx;
    char str[M];
    int q[N * S], ne[N * S];
    
    void insert()
    {
        int p = 0;
        for (int i = 0; str[i]; i ++ )
        {
            int t = str[i] - 'a';
            if (!tr[p][t]) tr[p][t] = ++ idx;//赋值一个结点
            p = tr[p][t];
        }
        cnt[p] ++ ;
    }
    
    void build()
    {
        int hh = 0, tt = -1;
        for (int i = 0; i < 26; i ++ )
            if (tr[0][i])
                q[ ++ tt] = tr[0][i];//队列 因为上面的操作 树已经建好了 这里看看根节点下面的对应字母 统计出第一层的各个结点
    
        while (hh <= tt)
        {
            int t = q[hh ++ ];
            for (int i = 0; i < 26; i ++ )
            {
                int p = tr[t][i];
                if (!p) tr[t][i] = tr[ne[t]][i];//如果不村子啊 直接跳到对应的最下方 下次直接用
                else
                {
                    ne[p] = tr[ne[t]][i];
                    q[ ++ tt] = p;
                }
            }
        }
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while (T -- )
        {
            memset(tr, 0, sizeof tr);
            memset(cnt, 0, sizeof cnt);
            memset(ne, 0, sizeof ne);
            idx = 0;
    
            scanf("%d", &n);
            for (int i = 0; i < n; i ++ )
            {
                scanf("%s", str);
                insert();
            }
    
            build();
    
            scanf("%s", str);
    
            int res = 0;
            for (int i = 0, j = 0; str[i]; i ++ )
            {
                int t = str[i] - 'a';
                j = tr[j][t];
    
                int p = j;
                while (p)
                {
                    res += cnt[p];
                    cnt[p] = 0;
                    p = ne[p];
                }
            }
    
            printf("%d/n", res);
        }
    
        return 0;
    }
    
    
    

    原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/281766.html

    (0)
    上一篇 2022年8月23日 07:15
    下一篇 2022年8月23日 07:15

    相关推荐

    发表回复

    登录后才能评论