HDU - 2896 病毒侵袭 (AC自动机,last优化)

题目链接:HDU - 2896

题意:给你n个模式串,对应每个模式串由编号,给出m个文本串,然后你要输出对应所匹配出模式串序号,以及有多少个文本串中有模式串

思路:比起比较基本统计个数,这里我们可以用set或者map来统计模式串序号

这里总结一下新学习的last优化。 参考博客:(1)

last相当于一个超级fail指针:

因为我们只有到根节点时才会重新匹配一个字母,所以我们此时直接记录一个last ,直接结束当前匹配过程.直接省去原 Fail 指针到可以匹配的节点之间的距离.

同时结合路径压缩,在匹配时可以完全不使用原 Fail.可以看下参考博客里面的图片。

code: 详细注解

#include<bits/stdc++.h>
const int N=100000+5;
using namespace std;

struct AC_automaton{
    int trie[N][128];//字典树
    int val[N];//字符串结尾标记
    int fail[N];//失配指针
    int last[N];//last[i]=j表j节点表示的单词是i节点单词的后缀,且j节点是单词节点(last优化)
    int tot;//编号
    void init(){//初始化0号点
        tot=1;
        val[0]=fail[0]=last[0]=0;
        memset(trie[0],0,sizeof(trie[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];
            if(trie[root][id]==0){
                trie[root][id]=tot;
                memset(trie[tot],0,sizeof(trie[tot]));
                val[tot++]=0;
            }
            root=trie[root][id];
        }
        val[root]=v; //编号
        //val[root]++; 个数
    }

    void build(){//构造fail与last
        queue<int> q;
        last[0]=fail[0]=0;
        //先把第0个部分放进去
        for(int i=0;i<128;i++){
            int root=trie[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();
            //ASCII编码范围
            for(int i=0;i<128;i++){
                int u=trie[k][i];//被取出结点k的子结点
                if(u==0)
                    continue;
                q.push(u);

                int v=fail[k];//k位置的失配指针
                //把子节点改成fail节点的子节点形成一个Trie图
                while(v && trie[v][i]==0)
                    v=fail[v];

                fail[u]=trie[v][i];//得到其儿子的失配结点
                //last指针表示“在它顶上的fail边所指向的一串节点中,第一个真正的结束节点”
                last[u]=val[fail[u]]?fail[u]:last[fail[u]];
            }
        }
    }

    void print(int i,set<int> &st){//递归找到存在结点i后缀相同的前缀节点编号
        if(val[i]){
            if( st.find(i)==st.end() )
                st.insert(val[i]);
            print(last[i],st);
        }
    }

    void query(char *s,set<int> &st){//匹配
        int len=strlen(s);
        int j=0;
        for(int i=0;i<len;i++){
            int id=s[i];
            while(j && trie[j][id]==0)
                j=fail[j];
            j=trie[j][id];
            if(val[j])
                print(j,st);
            else if(last[j])
                print(last[j],st);
        }
    }
}ac;
char P[N];
char T[N];
set<int>::iterator it;
int main(){
    int n;
    scanf("%d",&n);
    ac.init();
    for(int i=1;i<=n;i++){
        scanf("%s",P);
        ac.insert(P,i);
    }
    ac.build();

    int m;
    scanf("%d",&m);

    int total=0;
    for(int i=1;i<=m;i++){
        scanf("%s",&T);
        set<int> st;//保存文本串已经匹配到的模式串编号
        ac.query(T,st);
        if(!st.empty()){
            total++;

            printf("web %d:",i);
            for(set<int>::iterator it=st.begin();it!=st.end();it++)
                printf(" %d",(*it));
            printf("%\n");
        }
    }
    printf("total: %d\n",total);
    return 0;
}

原文地址:https://www.cnblogs.com/Tianwell/p/11376263.html

时间: 2024-10-18 18:27:44

HDU - 2896 病毒侵袭 (AC自动机,last优化)的相关文章

HDU 2896 病毒侵袭 AC自动机题解

本题是在text里面查找key word的增强版,因为这里有多个text. 那么就不可以简单把Trie的叶子标志记录修改成-1进行加速了,可以使用其他技术,我直接使用个vis数组记录已经访问过的节点,达到加速效果,速度还算挺快的. 不过看discuss里面有人直接使用Trie,做出了140ms的速度,而且他的程序严格来说并不正确,可见本题的数据很水啊.Trie的时间效率肯定比AC自动机低,但是在数据很水的特殊情况下,Trie的速度也可以很快的. 注意两个细节: 1 病毒也需要安装顺序输出,不小心

hdu 2896 病毒侵袭 AC自动机(查找包含哪些子串)

病毒侵袭 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 19465    Accepted Submission(s): 4814 Problem Description 当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻....在这样的时刻,人们却异常兴奋——我们能在有生之年看到500年一遇的世界奇观,那是多么幸福的事儿

[hdu 2896] 病毒侵袭 [ac自动机][病毒特征码匹配]

病毒侵袭 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 20728    Accepted Submission(s): 5058 Problem Description 当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻....在这样的时刻,人们却异常兴奋--我们能在有生之年看到500年一遇的世界奇观,那是多么幸福的事

HDU 2896 病毒侵袭 (AC自动机模板)

病毒侵袭 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 13063    Accepted Submission(s): 3378 Problem Description 当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻....在这样的时刻,人们却异常兴奋--我们能在有生之年看到500年一遇的世界奇观,那是多么幸福的事

HDU 2896 病毒侵袭(AC自动机模版题)

AC自动模版题,中文题目就不叙述题意了啊. AC自动主要是构造出字典树之后找到fail指针的跳转,类似于KMP里面的next数组的跳转啊,注意这里是多模式跳转.意思就是这个串跳到下一个串的什么位置啊. 先帖一下,做多了再一起总结吧. 病毒侵袭 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11347    Accepted Submi

HDU 2896 病毒侵袭(AC自动机)

解题思路: 对AC自动机算法的理解和应用,要注意的是,题目中的字符是128个ASCII码,而不是常用的26个英文字符,因此子节点的数目为128. #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <cmath> #include <cstring> #include <vector> #include &

HDU 2896 病毒侵袭

二次联通门 : HDU 2896 病毒侵袭 /* HDU 2896 病毒侵袭 AC自动机 此题太坑啦... 用G++交会无限MLE 行尾不输出回车会无限WA.. 写的程序没错 却交了N遍.. */ #include <cstring> #include <cstdio> #include <queue> #define Max 100090 void read (int &now) { now = 0; register char word = getchar

HDU 2896 病毒侵袭 (AC自动机)

题目链接:病毒侵袭 解析:利用end数组记录各病毒的编号,然后统计即可.注意此时sigma_size为128(0~127的ascii码可见字符). AC代码: #include <bits/stdc++.h> using namespace std; const int maxn = 502; const int max_word = 202; const int max_text = 10002; const int sigma_size = 128; struct Trie{ int ne

HDU 2896 病毒侵袭(AC自动机/模板题)

传送门 病毒侵袭 Time Limit: 2000/1000 MS (Java/Others)            Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 63941             Accepted Submission(s): 21264 Description 当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻....在这样的时刻,人们却异常兴奋——我们能在有生之年看到500年一