AC自动机(数组版) 及其优化

之前很喜欢用链表写AC自动机,觉得很适合自己的理解。一直都没有接触数组的写法。。

然而稍微难一点的AC自动机的题,大佬们都是用数组写的,搞的我也看不懂大佬的思路。。

所以觉得重新学习一下数组的写法,但是收获还是有的,又学习了一些关于AC自动机的小优化。

struct acAuto{
    int Next[Maxn][26] ;
    int num[Maxn], fail[Maxn] ;
    int cnt ;
    void init(){
        memset(Next, 0, sizeof(Next)) ;
        memset(num, 0, sizeof(num)) ;
        memset(fail, 0, sizeof(fail)) ;
        cnt = 0 ;
    }
    void Insert(string s ){
        int root = 0;
        for (auto i : s){
            int id = i - 'a' ;
            if (!Next[root][id]){
                Next[root][id] = ++cnt ;

            }
            root = Next[root][id] ;
        }
        num[root]++ ;
    }
    void get_fail(){
        queue < int > que ;
        for (int i = 0; i < 26; i++){
            if (Next[0][i]) {
                fail[Next[0][i]]= 0 ;
                que.push(Next[0][i]) ;
            }
        }
        while (!que.empty()){
            int tmp = que.front() ;
            que.pop() ;
            for (int i = 0; i < 26; i++){
                if (Next[tmp][i]){
                    fail[Next[tmp][i]] = Next[fail[tmp]][i] ;
                    que.push(Next[tmp][i]) ;
                }
                else Next[tmp][i] = Next[fail[tmp]][i] ;
            }
        }
    }
    int query (string s ){
        int root = 0 ;
        int ans = 0 ;
        for (auto i : s){
            int id = i - 'a' ;
            root = Next[root][id] ;
            for (int j = root; j && num[j] != -1; j = fail[j]){
                ans += num[j] ;
                num[j] = -1 ;
            }
        }
        return ans ;
    }
}AC ;

小优化:

1.类似于路径压缩。

if (Next[tmp][i]){
    fail[Next[tmp][i]] = Next[fail[tmp]][i] ;
    que.push(Next[tmp][i]) ;
}
else Next[tmp][i] = Next[fail[tmp]][i] ;

此步操作使得在比较时省去了 While 循环。
如果没有匹配,直接进入 Fail 的匹配之中。

2.后缀连接:

    void get_fail(){
        queue < int > que ;
        for (int i = 0; i < 26; i++){
            if (Next[0][i]) {
                fail[Next[0][i]]= 0 ;
                que.push(Next[0][i]) ;
            }
        }
        while (!que.empty()){
            int tmp = que.front() ;
            que.pop() ;
            for (int i = 0; i < 26; i++){
                int p = Next[tmp][i] ;
                if (Next[tmp][i]){
                    fail[Next[tmp][i]] = Next[fail[tmp]][i] ;
                    que.push(Next[tmp][i]) ;
                    last[p] = num[fail[p]] ? fail[p] : last[fail[p]] ;
                }
                else Next[tmp][i] = Next[fail[tmp]][i] ;
            }
        }
    }

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

这样的做法可以节省时间, 不过需要注意的是内存的限制!

3.树形DP优化。

树形DP优化的是查询部分.
首先我们可以发现, Fail 指针是绝对满足树形结构的.
显而易见,每个点的 Fail 都仅指向一个一个节点。

但是还没遇到此类优化问题,所以本人也不会。。。

你可能感兴趣的:(AC自动机)