• Gym 101741 K(AC自动机)


    传送门

    题意:

    给你一个长度为nn的模式串以及mm个总长度长度不超过10510^5的匹配串。问这mm个匹配串分别在模式串中出现了多少次,要求每一次出现位置不能够相交。

    题目分析:

    首先,我们要知道,虽然匹配串的总长度为10510^5,但是因为KMP的时间复杂度为O(n+m)mathcal{O}(n+m),这就使得整体复杂度总会变成O(n2)mathcal{O}{(n^2)},因此KMP显然是会超时的。

    因为涉及多串匹配的问题,因此我们可以往AC自动机方向去考虑。我们首先离线将询问的所有字符串建立出AC自动机,之后我们只需要用模式串在AC自动机上去匹配即可。

    但是该问题的特殊点在于:两个能够在模式串中匹配的串不能够有相交部分。对于这一点,我们只需要在普通的查询中进行修改;我们需要记录一下在AC自动机匹配到字符串strstr(我们设该状态在自动机上的ididii)的位置pos(i)pos(i),以及strstr前一个被匹配了的位置lastpos(i)lastpos(i)。我们可以发现,如果当前匹配的位置与之前匹配之差小于当前字符串的长度str|str|,即pos(i)lastpos(i)>=strpos(i)-lastpos(i)>=|str|则说明在当前位置pos(i)pos(i)下,能够对答案有贡献,我们使该匹配串的答案加11即可。

    最后我们只需要维护一下匹配串的终点位置,最后输出答案即可。

    代码:

    #include <bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    char st[maxn],st1[maxn];
    struct Trie{
        int next[maxn<<2][26],End[maxn<<2],root,fail[maxn<<2],id;
        int cnt[maxn<<2],last[maxn<<2],Len[maxn<<2];
        int newnode(){
            for(int i=0;i<26;i++){
                next[id][i]=-1;
            }
            Len[id]=0;
            return id++;
        }
        void init(){
            id=0;
            root=newnode();
        }
        void Insert(char *str,int id){
            int len=strlen(str);
            int now=root;
            for(int i=0;i<len;i++){
                if(next[now][str[i]-'a']==-1)
                    next[now][str[i]-'a']=newnode();
                Len[next[now][str[i]-'a']]=Len[now]+1;
                now=next[now][str[i]-'a'];
            }
            End[id]=now;
            Len[now]=len;
        }
        void build(){
            queue<int>que;
            int now=root;
            fail[root]=root;
            for(int i=0;i<26;i++){
                if(next[root][i]==-1){
                    next[root][i]=root;
                }
                else{
                    fail[next[root][i]]=root;
                    que.push(next[root][i]);
                }
            }
            while(!que.empty()){
                now=que.front();
                que.pop();
                for(int i=0;i<26;i++){
                    if(next[now][i]==-1)
                        next[now][i]=next[fail[now]][i];
                    else{
                        fail[next[now][i]]=next[fail[now]][i];
                        que.push(next[now][i]);
                    }
                }
            }
        }
        void query(char *str){
            int len=strlen(str);
            for(int i=0;i<id;i++) cnt[i]=0,last[i]=-1;
            int now=root;
            for(int i=0;i<len;i++){
                now=next[now][str[i]-'a'];
                int tmp=now;
                while(tmp!=root){
                    if(i-last[tmp]>=Len[tmp]){
                        cnt[tmp]++;
                        last[tmp]=i;
                    }
                    tmp=fail[tmp];
                }
            }
        }
    }ac;
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        ac.init();
        scanf("%s",st);
        for(int i=1;i<=m;i++){
            scanf("%s",st1);
            ac.Insert(st1,i);
        }
        ac.build();
        ac.query(st);
        for(int i=1;i<=m;i++){
            printf("%d
    ",ac.cnt[ac.End[i]]);
        }
        return 0;
    }
    
  • 相关阅读:
    hdu1814 Peaceful Commission 2-SAT
    上传下载文件
    文件下载类
    文件操作类
    MD5加密帮助类
    加密解密类
    发送邮件函数
    DataTable 分页
    服务器缓存帮助类
    Cookie帮助类
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007160.html
Copyright © 2020-2023  润新知