AC自动机&后缀自动机

匿名 (未验证) 提交于 2019-12-02 23:47:01

理解的不够深 故只能以此来加深理解 。我这个人就是蠢没办法 学长讲的题全程蒙蔽。可能我字符串就是菜吧,哦不我这个人就是菜吧。

AC自动机的名字 AC 取自一个大牛 而自动机就比较有讲究了 不是寻常的东西呢。

自动机由5部分组成 1 字符集 2 状态集合 3 初始状态 4 结束状态集合 5 状态转移函数。

字符集 是指自动机字符的集合。 当然以上有点深奥,我们只需要其能识别字符串即可。

显然的是 KMP做单字符串对单字符串的匹配使用 而AC自动机则是多个字符串在一个字符串上的匹配。

构建trie 大家都会 但是如何求fail 指针呢 思考一下我们求的fail指针其实是指向和当前这个串能匹配的最长后缀。前缀是没有必要的因为我是利用答案串进行匹配的,因为我们得到一个最长前缀再继续这样匹配还是一样效果我们既然答案串都匹配到了现在 也就是当前局面已经不可逆了我们只能不断的跳 跳到一个合法地方让其能继续匹配下去 所以跳到一个最长后缀的位置这样如果还失配的话我们还有机会继续跳 如果跳到一个较短的后缀上的话我们把那些长的都扔了这显然是非常不好做法。

那么现在就有了基础思路构造fail指针 然后失配就不断跳 就行了。在构造fail指针的时候我们是先构造了一颗trie树在trie树搞fail指针显然最长后缀<当前匹配的位置 至于那些比当前位置还要深的位置一定不可能出现,那么就是看深度咯 bfs逐层扩展的过程中 沿着父亲的fail指针走即可。

//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define EPS 1e-8 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() {     return (fs==ft&&(ft=(fs=buf))+fread(buf,1,1<<15,stdin),fs==ft)?0:*fs++; } inline int read() {     int x=0,f=1;char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } const int MAXN=1000010; char a[MAXN]; int n,root,len,cnt,T,h,ans; int t[MAXN][26],e[MAXN]; int q[MAXN],fail[MAXN]; inline void insert(char *c) {     int p=root;     //cout<<c[1]<<' '<<c[2]<<endl;     for(int i=1;i<=len;++i)     {         int w=c[i]-'a';         if(!t[p][w])t[p][w]=++cnt;         p=t[p][w];     }     ++e[p]; } inline void get_fail() {     T=h=0;     for(int i=0;i<=25;++i)     {         if(t[root][i])         {             q[++T]=t[root][i];             fail[t[root][i]]=0;         }     }     while(h++<T)     {         int tn=q[h];         for(int i=0;i<=25;++i)         {             if(t[tn][i])             {                 q[++T]=t[tn][i];                 fail[t[tn][i]]=t[fail[tn]][i];             }             else t[tn][i]=t[fail[tn]][i];         }     } } inline void AC() {     int p=root;     for(int i=1;i<=len;++i)     {         int c=a[i]-'a';         int w=t[p][c];         p=t[p][c];         while(w&&e[w]!=-1)         {             ans+=e[w];             e[w]=-1;             w=fail[w];         }     } } int main() {     //freopen("1.in","r",stdin);     n=read();     for(int i=1;i<=n;++i)     {         scanf("%s",a+1);         len=strlen(a+1);         insert(a);     }     get_fail();     scanf("%s",a+1);     len=strlen(a+1);     AC();     printf("%d",ans);     return 0; }
View Code

自己的第二份AC自动机代码这次真的是理解了 因为后缀数组后缀自动机把我搞自闭了。

//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define EPS 1e-8 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() {     return (fs==ft&&(ft=(fs=buf))+fread(buf,1,1<<15,stdin),fs==ft)?0:*fs++; } inline int read() {     int x=0,f=1;char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}     return x*f; } const int MAXN=2000010; char a[MAXN]; int n,root,len,cnt,T,h; int t[MAXN][26],vis[MAXN]; int q[MAXN],fail[MAXN],ans[MAXN]; inline void insert(char *c,int v) {     int p=root;     //cout<<c[1]<<' '<<c[2]<<endl;     for(int i=1;i<=len;++i)     {         int w=c[i]-'a';         if(!t[p][w])t[p][w]=++cnt;         p=t[p][w];     }     vis[v]=p; } inline void get_fail() {     T=h=0;     for(int i=0;i<=25;++i)     {         if(t[root][i])         {             q[++T]=t[root][i];             fail[t[root][i]]=0;         }     }     while(h++<T)     {         int tn=q[h];         for(int i=0;i<=25;++i)         {             if(t[tn][i])             {                 q[++T]=t[tn][i];                 fail[t[tn][i]]=t[fail[tn]][i];             }             else t[tn][i]=t[fail[tn]][i];         }     } } inline void AC() {     int p=root;     for(int i=1;i<=len;++i)     {         int c=a[i]-'a';         p=t[p][c];++ans[p];     }     for(int i=T;i>=1;--i)ans[fail[q[i]]]+=ans[q[i]]; } int main() {     freopen("1.in","r",stdin);     n=read();     for(int i=1;i<=n;++i)     {         scanf("%s",a+1);         len=strlen(a+1);         insert(a,i);     }     get_fail();     scanf("%s",a+1);     len=strlen(a+1);     AC();     for(int i=1;i<=n;++i)printf("%d\n",ans[vis[i]]);     return 0; }
View Code

发现可以直接AC自动机 然后 树上差分一下即可。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!