Luogu P3786 AC自动机加强版___AC自动机

题目大意:

有N 个由小写字母组成的模式串以及一个文本串T。
每个模式串可能会在文本串中出现多次。
你需要找出哪些模式串在文本串TT 中出现的次数最多。

1≤N≤150
模式串长度≤70
文本串T长度≤10^6

题解:

就是在这题的基础上改一下
http://blog.csdn.net/gx_man_vip/article/details/79556985
对于每个end[i]
(点i作为单词末位在所有单词中出现了多少次)
我们因为要记录次数,所以用rp[i]去每次找到时去累加end[i]。
然后每次存下单词末位在trie中的位置,
最后根据这些位置去找一个最大值

然后数组清空的问题呢,因为你的trie树数组很大,直接清空显然时间很不优美,所以我是对能拓展到的点才进行清零。

代码:

#include<bits/stdc++.h>
#define N 50010

using namespace std;
queue <int> Q;

int n,maxn,num,next[N][26],fail[N],end[N],rp[N],a[N];
char s[151][71],op[1000005];

void insert(int dep,char *s)
    {
        int len=strlen(s);
        int u=0;
        for (int i=0; i<len; i++)
        {
             int v=s[i]-'a';
             if (!next[u][v]) 
                 {
                       next[u][v]=++num;
                       memset(next[num],0,sizeof(next[num]));
                 }
             u=next[u][v];
        }
        end[u]++;
        a[dep]=u;
    }

void build()
    {
        fail[0]=0;
        for (int i=0; i<26; i++)
            if (next[0][i]) 
                 {
                          fail[next[0][i]]=0;
                          Q.push(next[0][i]);
                 }      

        while (!Q.empty())
        {
            int now=Q.front();
            Q.pop();
            for (int i=0; i<26; i++)
                 if (!next[now][i]) 
                      next[now][i]=next[fail[now]][i];
                      else {
                               fail[next[now][i]]=next[fail[now]][i];
                               Q.push(next[now][i]);
                           }
        }
    }

void query(char *s)
    {
        int len=strlen(s);
        int now=0;
        for (int i=0; i<len; i++)
        {
            int v=s[i]-'a';
            now=next[now][v];
            int u=now;
            while (u!=0)
            {
                   rp[u]=rp[u]+end[u];
                   u=fail[u];
            }               
        }
    }

int main()
{
        scanf("%d",&n);
        while (n)
        {           
             memset(next[0],0,sizeof(next[0]));
             for (int i=0; i<=num; i++)
                  {
                       rp[i]=0;
                       end[i]=0;
                       fail[i]=0;
                  }
             num=0; maxn=0;
             for (int i=1; i<=n; i++)
                  {
                      scanf("%s",s[i]);
                      insert(i,s[i]);
                  }
             build();
             scanf("%s",op);
             query(op);
             for (int i=1; i<=num; i++)
                 if (rp[i]>maxn) maxn=rp[i];
             printf("%d\n",maxn);
             for (int i=1; i<=n; i++)
                  if (rp[a[i]]==maxn) printf("%s\n",s[i]);

             scanf("%d",&n);
        }
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值