JSK 习题:蒜厂工作手册-AC自动机

这里写图片描述

思路:

首先对模式串构建AC自动机,在建立Trie时标记模式串的终止结点,并记录每个串的长度,在AC自动机上对原串进行匹配,同时将每个状态加入栈中,若后续成功匹配,则将状态出栈至加入该串之前的状态,再从栈顶状态开始继续匹配,重复该步骤,直到匹配到原串结尾,最后栈中保存的即为答案。

注意:

使用string会发生段错误,改为char[]就没问题了,原因未知。。。

代码:

#include <bits/stdc++.h>
using namespace std;

const int MAXN=1000005;
const int MAXC=26;

int p=0;
struct AC_Automaton {
    int trie[MAXN][MAXC], fail[MAXN], sta[MAXN],cnt[MAXN],last[MAXN];
    int tot;
    queue<int>Q;
    void init() {
        while(!Q.empty()) Q.pop();
        memset(trie, -1, sizeof(trie));
        memset(fail, 0, sizeof(fail));
        tot = 0;
        memset(sta, 0, sizeof(sta));
        memset(cnt,0,sizeof(cnt));
        memset(last,0,sizeof(last));
        memset(S,0,sizeof(S));
    }

    void insert(char *ch) {              //插入字符串到字典树中
        int rt = 0,i;
        for (i = 0; ch[i]; i++)
        {
            if (trie[rt][ch[i] - 'a'] == -1) trie[rt][ch[i] - 'a']= ++tot;
            rt = trie[rt][ch[i] - 'a'];
        }
            sta[rt]=i;           //记录该串长度
            cnt[rt]++;           //标记该串终止结点
    }


    void build()           //构建fail指针数组,相当于next数组,利用bfs求
    {
        for(int i=0;i<MAXC;i++)       //初始化
            if(trie[0][i]==-1)
                trie[0][i]=0;         //将不存在的点指向根节点
            else
                Q.push(trie[0][i]);    //将与根节点直接相连的点入队

        while(!Q.empty())
        {
            int rt=Q.front();
            Q.pop();
            for(int i=0;i<MAXC;i++)
            {
                if(trie[rt][i]==-1)           //某一点无后续节点,将其连向失配指针所在位置
                    trie[rt][i]=trie[fail[rt]][i];
                else                            //有后续节点
                {
                    int v=fail[rt];
                    int u=trie[rt][i];
                    while(v && trie[v][i]==-1) v=fail[v]; //其失配指针是从其父亲失配指针指向位置向后搜索i,若有则连接
                    fail[u]=trie[v][i];
                    Q.push(u);
                    last[u]=cnt[u]?u:last[fail[u]];       //若失配存储失配后的位置,若匹配成功,存储当前结点
                }
            }
        }
    }
    int S[MAXN];     //模拟栈

    void solve(char *ch)
    {
        int rt=0,top=0;
        for(int i=0;ch[i];i++)
        {
            ch[p++]=ch[i];             //p为当前位置指针,
            rt=trie[rt][ch[i]-'a'];
            S[top++]=rt;             //将当前状态入栈
            int tmp=last[rt];

            if(cnt[tmp]>0)         //若匹配成功
            {
                p-=sta[tmp];        //将匹配成功的串出栈
                top-=sta[tmp];
                rt=S[top-1];        //从栈顶元素开始重新匹配
            }
        }
        return ;
    }
}T;

int main()
{
    char a[100005];
    char s[100005];
    int n;
    T.init();
    scanf("%s",s);

    cin>>n;
    while(n--)
    {
        scanf("%s",a);
        T.insert(a);
    }
    T.build();
    T.solve(s);
    s[p]='\0';
    printf("%s\n",s);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43093481/article/details/82387662