这像是能解决所有问题的样子(并不)。AC自动机之所以叫AC自动机是因为它能解决所有AC自动机的题。

其实只能解决的是很多模式串匹配一个母串的问题。

把kmp中的next数组得到下一次跳转的位置看成特殊的边,把字符串看成链,就会得到一个特殊的图。

一个点u的next连向点v对应的字符串是u最长的后缀,把这个图套用到很多个模式串组成的trie上。

对于点u能走到的节点ch[u][i],相当于在u对应的字符串后补了一个字符i。那么点u的next连向的点v对应的字符串后面再补一个字符i就是点ch[u][i]的next。有时并没有ch[v][i],那么就要找v的next也就是更短的后缀。要是一直找不到,就只能连向0号点(空串)了。算next是一定要用bfs,不然会出现要走u的next却发现u的next还没有被算出的尴尬情况。

匹配时,如果没有ch[u][i]就要走u的next。还要注意的是,能走到u说明也能走到u的next、u的next的next、u的next的next的next等,要把这些点的值也加上。这样不会TLE吗?想必是不会的。要想知道一堆字符串中有多少个是某个串的子串,走到每个字符串结尾代表的节点时,只能统计一次。那么在统计时可以进行标记,被标记的点就不走了。又因为当一个点被标记时,它的next、它的next的next等都已经被统计过了,所以当顺着next统计的时候,发现一个被标记的点就可以停止了。自动机上的每一个点只会走到一次,所以并不会TLE。

考虑next很麻烦?当ch[u][i]==0时,令ch[u][i]=next!

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#define maxn 1000010
#define rep(i,x,y) for(register int i=(x);i<=(y);i++)
#define dwn(i,x,y) for(register int i=(x);i>=(y);i--)
#define vv ch[u][i]
using namespace std;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(isdigit(ch)==0 && ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void write(int x)
{
    int f=0;char ch[20];
    if(!x){puts("0");return;}
    if(x<0){putchar('-');x=-x;}
    while(x)ch[++f]=x%10+'0',x/=10;
    while(f)putchar(ch[f--]);
    putchar('\n');
}
int ch[maxn][30],val[maxn],fail[maxn],cnt=0,n,len;
char s[maxn];
int gx(char c){return c-'a';}
void build()
{
    int u=0;
    rep(i,0,len-1)
    {
        if(!ch[u][gx(s[i])])ch[u][gx(s[i])]=++cnt;
        u=ch[u][gx(s[i])];
    }
    val[u]++;
}
void getfail()
{
    fail[0]=0;
    queue<int >q;
    rep(i,0,25)
        if(ch[0][i])
            q.push(ch[0][i]),
            fail[ch[0][i]]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        rep(i,0,25)
        {
            if(vv)
                q.push(vv),fail[vv]=ch[fail[u]][i];
            else vv=ch[fail[u]][i];
        }
    }
}
int getans()
{
    int ans=0,u=0;
    rep(i,0,len-1)
    {
        u=ch[u][gx(s[i])];
        for(int j=u;j&&val[j]!=-1;j=fail[j])
            ans+=val[j],val[j]=-1;
    }
    return ans;
}
int main()
{
    n=read();
    rep(i,1,n)
        scanf("%s",s),len=strlen(s),build();
    getfail();
    scanf("%s",s),len=strlen(s);write(getans());
    return 0;
}
View Code

相关文章:

  • 2021-12-15
  • 2021-10-29
  • 2021-11-18
  • 2021-06-03
  • 2021-12-26
  • 2022-02-08
  • 2021-08-02
  • 2022-02-17
猜你喜欢
  • 2022-02-15
  • 2021-07-13
  • 2022-03-04
  • 2021-11-30
  • 2021-07-28
  • 2021-11-28
相关资源
相似解决方案