每日总结-05-19(AC自动机结束)

今天下午讨论了一下校赛的题,终于最终拍板,把校赛的题目定下来了。

然后今天A掉了4个AC自动机的题目。终于完成了AC自动机专辑里面的15个题。至此AC自动机完全结束。

明天开启线段树专题。。。。。

------------------------------------------------------------------------------------------------------------------------------------

1,,hdu-2457-DNA repair

题目:给出一些不合法的模式DNA串,给出一个原串,问最少需要修改多少个字符,使得原串中不包含非法串

做法:把病毒串做成一个trie树。

DP[i][j]:长度为i,在trie树上的状态为j,需要改变的最少的字符的个数。

dp[i][j]=dp[i-1][k]+转移费用。

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<string.h>
#include<math.h>
using namespace std;
#define LL __int64
#define maxn 55
const int maxnode=55*21;
const int childnum=5;
const int mod=1000000007;
const int INF=99999999;
struct ac_tree
{
    int chd[maxnode][childnum];
    int val[maxnode];
    int fail[maxnode];
    int key[1<<11];
    int Q[maxnode];
    int ID[128];
    int sz;
    int dp[1100][maxnode];
    void init()
    {
        fail[0]=0;
        for(int i=0;i<childnum;i++)
        {
            ID[i]=i;
        }
        ID[‘A‘]=1;ID[‘G‘]=2;
        ID[‘C‘]=3;ID[‘T‘]=4;
    }
    void reset()
    {
        memset(chd,0,sizeof(chd));
        sz=1;
    }
    void insert(char str[],int k)
    {
        int p=0;
        int len=strlen(str);
        for(int i=0;i<len;i++)
        {
            int c=ID[str[i]];
            if(!chd[p][c])
            {
                memset(chd[sz],0,sizeof(chd[sz]));
                val[sz]=0;
                chd[p][c]=sz++;
            }
            p=chd[p][c];
        }
        val[p]=k;
    }
    void ac_build()
    {
        int *s=Q,*e=Q;
        for(int i=1;i<childnum;i++)
        {
            if(chd[0][i])
            {
                fail[chd[0][i]]=0;
                *e++=chd[0][i];
            }
        }
        while(s!=e)
        {
            int u=*s++;
            if(val[fail[u]])val[u]+=val[fail[u]];
            for(int i=1;i<childnum;i++)
            {
                int &v=chd[u][i];
                if(v)
                {
                    *e++=v;
                    fail[v]=chd[fail[u]][i];
                  //  val[v]=(val[v]+val[fail[v]]);
                }
                else v=chd[fail[u]][i];
            }
        }
    }
    int work(char str[])
    {
        int len=strlen(str);
        int i,j,k;
        for(i=0;i<=len;i++)
            for(j=0;j<sz;j++)dp[i][j]=INF;
        dp[0][0]=0;
        int cr,x;
        for(i=0;i<len;i++)
        {
            for(j=0;j<sz;j++)
            {
                if(dp[i][j]==INF)continue;
                for(k=1;k<childnum;k++)
                {
                    cr=chd[j][k];
                    if(val[cr])continue;
                    x=dp[i][j];
                    if(k!=ID[str[i]])x++;
                    dp[i+1][cr]=min(dp[i+1][cr],x);
                }
            }
        }
        int maxx=INF;
        for(j=0;j<sz;j++)
        {
            maxx=min(maxx,dp[len][j]);
        }
        if(maxx==INF)cout<<"-1"<<endl;
        else cout<<maxx<<endl;
    }
}AC;
char tmp[1550];
int main()
{
    AC.init();
    int n,m,k,x;
    int T;
    T=0;
    while(~scanf("%d",&n)&&(n))
    {
        T++;
        AC.reset();
        for(int i=1;i<=n;i++)
        {

            scanf("%s",tmp);
            AC.insert(tmp,1);
        }
        AC.ac_build();
        scanf("%s",tmp);
        printf("Case %d: ",T);
        AC.work(tmp);
    }
    return 0;
}

2,zoj-3228-Searching the String

题目大意:给出一个字符串和若干个单词,问这些单词在字符串里面出现了多少次。单词前面为0表示这个单词可重叠出现,1为不可重叠出现。

做法: 可重叠的很好处理,就是AC自动机处理。不可重叠的就记录下上次这个单词出现的位置,如果上次出现的位置加上这个单词的长度小于等于当前位置的话,那么这个单词的次数就加一。否则就不加。

注意:可能存在两个字符串相等,比较坑爹。。。

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<string.h>
#include<math.h>
using namespace std;
#define LL __int64
#define maxn 55
const int maxnode=6*110000;
const int childnum=27;
const int mod=1000000007;
const int INF=99999999;
struct ac_tree
{
    int chd[maxnode][childnum];
    int val[maxnode][2];
    int fail[maxnode];
    int last[maxnode];
    int num[maxnode][2];
    int *ans[maxnode];
    int key[1<<11];
    int Q[maxnode];
    int ID[128];
    int sz;
    void init()
    {
        fail[0]=0;
        for(int i=0;i<childnum;i++)
        {
            ID[i+‘a‘]=i+1;
        }
    }
    void reset()
    {
        memset(chd,0,sizeof(chd));
        memset(num,0,sizeof(num));
        sz=1;
    }
    void insert(char str[],int k,int tt)
    {
        int p=0;
        int len=strlen(str);
        for(int i=0;i<len;i++)
        {
            int c=ID[str[i]];
            if(!chd[p][c])
            {
                memset(chd[sz],0,sizeof(chd[sz]));
                val[sz][tt]=0;
                chd[p][c]=sz++;
            }
            p=chd[p][c];
        }
        val[p][tt]=len;
        last[p]=-1;
        ans[k]=&num[p][tt];
    }
    void ac_build()
    {
        int *s=Q,*e=Q;
        for(int i=1;i<childnum;i++)
        {
            if(chd[0][i])
            {
                fail[chd[0][i]]=0;
                *e++=chd[0][i];
            }
        }
        while(s!=e)
        {
            int u=*s++;
            //if(val[fail[u]])val[u]+=val[fail[u]];
            for(int i=1;i<childnum;i++)
            {
                int &v=chd[u][i];
                if(v)
                {
                    *e++=v;
                    fail[v]=chd[fail[u]][i];
                  //  val[v]=(val[v]+val[fail[v]]);
                }
                else v=chd[fail[u]][i];
            }
        }
    }
    int work(char str[],int n)
    {
        int len=strlen(str);
        int root=0,key,tp,x;
        for(int i=0;i<len;i++)
        {
            tp=ID[str[i]];
            root=chd[root][tp];
            key=root;
            while(key!=0)
            {
                if(val[key][0]!=0)num[key][0]++;
                if(val[key][1]!=0)
                {
                    if(last[key]<=i-val[key][1])
                    {
                        num[key][1]++;
                        last[key]=i;
                    }
                }
                key=fail[key];
            }
        }
        for(int i=1;i<=n;i++)
            printf("%d\n",*ans[i]);
        puts("");
    }
}AC;
char tmp[1550];
char as[110000];
int main()
{
    AC.init();
    int n,m,k,x;
    int T;
    T=0;
    while(~scanf("%s",as))
    {
        T++;
        AC.reset();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d %s",&k,tmp);
            AC.insert(tmp,i,k);
        }
        AC.ac_build();
        printf("Case %d\n",T);
        AC.work(as,n);
    }
    return 0;
}

3,hdu-3341-Lost‘s revenge

抄的解题报告,感觉说的很详细,我就不赘余了。。

 题意:给出一些病毒串,一个原串,问怎么调整原串的顺序使得跟最多的病毒串匹配

    抄解题报告:

    自动机+DP。设主串有na个A、nc个C、ng个G、nt个T,于是题意转化为用na个A、

    nc个C、ng个G、nt个T生成一个新串,使模式串匹配次数最大。先将模式串生成自动机

    〔trie图〕,然后在这上面进行DP,状态可表示为dp[d,s],d为自动机状态,

    s表示由ma个A、mc个C、mg个G、mt个T的生成串

    〔s可表示为ma*(nc+1)*(ng+1)*(nt+1)+mc*(ng+1)*(nt+1)+mg*(nt+1)+mt〕,

    转移为O(4),总复杂度O(500*11^4*4)

    那种表示方法就是变进制吧

    直接DP(root,s)会超时

    Qinz说这题卡时紧

    我用他的那种dfs才能过,他是先dfs枚举出状态,然后对这个状态递推计算,好快

    比较神奇,不用管顺序的,就记录个数

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<string.h>
#include<math.h>
using namespace std;
#define LL __int64
#define maxn 55
const int maxnode=51*10;
const int childnum=5;
const int mod=1000000007;
const int INF=99999999;
struct ac_tree
{
    int chd[maxnode][childnum];
    int val[maxnode];
    int fail[maxnode];
    int Q[maxnode];
    int ID[128];
    int sz;
    int dp[maxnode][14642];
    void init()
    {
        fail[0]=0;
        for(int i=0;i<childnum;i++)
        {
            ID[i+‘a‘]=i+1;
        }
        ID[‘A‘]=1;ID[‘T‘]=2;
        ID[‘G‘]=3;ID[‘C‘]=4;
    }
    void reset()
    {
        memset(chd,0,sizeof(chd));
        memset(val,0,sizeof(val));
        sz=1;
    }
    void insert(char str[],int k)
    {
       // cout<<str<<endl;
        int p=0;
        int len=strlen(str);
        for(int i=0;i<len;i++)
        {
            int c=ID[str[i]];
            if(!chd[p][c])
            {
                memset(chd[sz],0,sizeof(chd[sz]));
                val[sz]=0;
                chd[p][c]=sz++;
            }
            p=chd[p][c];
        }
        val[p]+=k;
        //cout<<p<<" "<<val[p]<<endl;
    }
    void ac_build()
    {
        int *s=Q,*e=Q;
        for(int i=1;i<childnum;i++)
        {
            if(chd[0][i])
            {
                fail[chd[0][i]]=0;
                *e++=chd[0][i];
            }
        }
        while(s!=e)
        {
            int u=*s++;
            if(val[fail[u]])val[u]+=val[fail[u]];
            for(int i=1;i<childnum;i++)
            {
                int &v=chd[u][i];
                if(v)
                {
                    *e++=v;
                    fail[v]=chd[fail[u]][i];
                  //  val[v]=(val[v]+val[fail[v]]);
                }
                else v=chd[fail[u]][i];
            }
        }
    }
    int work(char str[])
    {
        int num[5];
        memset(num,0,sizeof(num));
        int len=strlen(str);
        for(int i=0;i<len;i++)num[ID[str[i]]]++;
        dp[0][0]=0;
        int tai,cr;
        int sum[6];
        sum[5]=1;
        sum[4]=num[4]+1;
        sum[3]=(num[3]+1)*sum[4];
        sum[2]=(num[2]+1)*sum[3];
        sum[1]=(num[1]+1)*sum[2];
        int a[5],i,j;
        int pt;
        memset(dp,-1,sizeof(dp));
        dp[0][0]=0;
        for(a[1]=0;a[1]<=num[1];a[1]++)
        for(a[2]=0;a[2]<=num[2];a[2]++)
        for(a[3]=0;a[3]<=num[3];a[3]++)
        for(a[4]=0;a[4]<=num[4];a[4]++)
        {
            if(a[1]+a[2]+a[3]+a[4]>=len)break;
            tai=0;
            for(i=1;i<=4;i++)tai+=a[i]*sum[i+1];
            for(i=0;i<sz;i++)
            {
                if(dp[i][tai]==-1)continue;
                for(j=1;j<childnum;j++)
                {
                    if(a[j]+1>num[j])continue;
                    cr=chd[i][j];
                    pt=tai+sum[j+1];
                   // if(dp[cr][pt]==-1)dp[cr][pt]=dp[i][tai];
                    dp[cr][pt]=max(dp[cr][pt],dp[i][tai]+val[cr]);
                   // cout<<i<<" "<<j<<"->"<<cr<<" "<<pt<<" "<<dp[cr][pt]<<endl;
                }
            }
        }
        pt=num[1]*sum[2]+num[2]*sum[3]+num[3]*sum[4]+num[4];
        int maxx=-1;
        for(i=0;i<sz;i++)
        {
            maxx=max(maxx,dp[i][pt]);
        }
        cout<<maxx<<endl;
    }
}AC;
char tmp[1550];
char as[110000];
int main()
{
    AC.init();
    int n,m,k,x;
    int T;
    T=0;
    while(~scanf("%d",&n)&&n)
    {
        T++;
        AC.reset();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",tmp);
            AC.insert(tmp,1);
        }
        AC.ac_build();
        scanf("%s",as);
        printf("Case %d: ",T);
        AC.work(as);
    }
    return 0;
}

4,hdu-3247-Resource Archiver

题意:

给你 n 个串,和 m 个病毒串。要把这 n 个串
连起来来,可以重叠,但不能包含 m 个病毒串
中的任意一个。求把 n 个串连起来的最小长度。

做法:这个题目憋了我很长时间,一直不知道怎么样处理。后来经过翻阅博客,终于悟出来点。

把病毒串和非病毒串都标记在trie树上处理。然后病毒串的地方标记为病毒串,非病毒串的地方标记为是哪一个串。状态压缩一下。

然后从每个非病毒串的结尾开始bfs。

map[i][j]:从i字符串的结尾,到j字符串的结尾的最短路径是多少。

然后就可以DP了。

dp[i][j]:已用的字符串为i状态,在trie树上的点为j状态,的最短路径。

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<string.h>
#include<math.h>
using namespace std;
#define LL __int64
#define maxn 205
const int maxnode=100001;
const int childnum=3;
const int mod=1000000007;
const int INF=99999999;
struct list
{
    int st;
    int x;
}pp,qq;
struct ac_tree
{
    int chd[maxnode][childnum];
    int val[maxnode];
    int fail[maxnode];
    int Q[maxnode];
    int var[maxnode];
    int vis[maxnode];
    int dist[maxnode];
    int st[maxn];
    int ID[128];
    int sz;
    int maps[maxn][maxn];
    int ns;
    int dp[1<<11][maxn];
    void init()
    {
        fail[0]=0;
        for(int i=0;i<childnum;i++)
        {
            ID[i+‘a‘]=i+1;
        }
        ID[‘0‘]=1;ID[‘1‘]=2;
    }
    void reset()
    {
        memset(chd,0,sizeof(chd));
        memset(val,0,sizeof(val));
        memset(var,0,sizeof(var));
        memset(maps,-1,sizeof(maps));
        sz=1;
    }
    void insert(char str[],int k,int ps)
    {
       // cout<<str<<endl;
        int p=0;
        int len=strlen(str);
        for(int i=0;i<len;i++)
        {
            int c=ID[str[i]];
            if(!chd[p][c])
            {
                memset(chd[sz],0,sizeof(chd[sz]));
                val[sz]=0;
                chd[p][c]=sz++;
            }
            p=chd[p][c];
        }
        if(ps==0)
        {
            val[p]=k;
            var[p]=0;
        }
        else var[p]=1;
        //cout<<p<<" "<<val[p]<<endl;
    }
    void ac_build()
    {
        int *s=Q,*e=Q;
        for(int i=1;i<childnum;i++)
        {
            if(chd[0][i])
            {
                fail[chd[0][i]]=0;
                *e++=chd[0][i];
            }
        }
        while(s!=e)
        {
            int u=*s++;
            //if(val[fail[u]]==-1)val[u]=-1;
            for(int i=1;i<childnum;i++)
            {
                int &v=chd[u][i];
                if(v)
                {
                    *e++=v;
                    fail[v]=chd[fail[u]][i];
                    val[v]=(val[v]|val[fail[v]]);
                    var[v]=(var[v]|var[fail[v]]);
                }
                else v=chd[fail[u]][i];
            }
        }
    }
    int bfs(int u)
    {
        queue<int>que;
        while(!que.empty())que.pop();
        int x=st[u];
        que.push(x);
        memset(dist,-1,sizeof(dist));
        dist[x]=0;
        while(!que.empty())
        {
            x=que.front();
            que.pop();
            for(int i=1;i<childnum;i++)
            {
                int y=chd[x][i];
                if(var[y])continue;
                if(dist[y]<0)
                {
                    dist[y]=dist[x]+1;
                    que.push(y);
                }
            }
        }
        for(int i=0;i<ns;i++)
        {
            maps[u][i]=dist[st[i]];
            //cout<<u<<"->"<<i<<"="<<maps[u][i]<<endl;
        }
    }
    int work(int n)
    {
        st[0]=0;
        ns=1;
        for(int i=0;i<sz;i++)
            if(val[i])
            {
                st[ns++]=i;
                //cout<<ns-1<<" "<<val[st[ns-1]]<<endl;
            }
        for(int i=0;i<ns;i++)bfs(i);
        memset(dp,-1,sizeof(dp));
        dp[0][0]=0;
        int cr;
        for(int i=0;i<(1<<n);i++)
        {
            for(int j=0;j<ns;j++)
            {
                if(dp[i][j]<0)continue;
                for(int k=0;k<ns;k++)
                {
                    if(maps[j][k]<0)continue;
                    cr=i|val[st[k]];
                    if(dp[cr][k]==-1)dp[cr][k]=dp[i][j]+maps[j][k];
                    else dp[cr][k]=min(dp[cr][k],dp[i][j]+maps[j][k]);
                   // cout<<cr<<" "<<k<<" "<<dp[cr][k]<<endl;
                }
            }
        }
        int minn=-1;
        cr=(1<<n)-1;
        for(int i=0;i<ns;i++)
        {
            if(dp[cr][i]==-1)continue;
            if(minn<0)minn=dp[cr][i];
            minn=min(minn,dp[cr][i]);
        }
        cout<<minn<<endl;
    }

}AC;
char tmp[1550];
int main()
{
    AC.init();
    int n,m,k,x;
    int T;
    T=0;
    while(~scanf("%d%d",&n,&m)&&(n||m))
    {
        AC.reset();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",tmp);
            AC.insert(tmp,1<<(i-1),0);
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%s",tmp);
            AC.insert(tmp,1,1);
        }
        AC.ac_build();
        AC.work(n);
    }
    return 0;
}

每日总结-05-19(AC自动机结束),布布扣,bubuko.com

时间: 2024-10-10 22:41:28

每日总结-05-19(AC自动机结束)的相关文章

经典算法题每日演练——第八题 AC自动机

原文:经典算法题每日演练--第八题 AC自动机 上一篇我们说了单模式匹配算法KMP,现在我们有需求了,我要检查一篇文章中是否有某些敏感词,这其实就是多模式匹配的问题. 当然你也可以用KMP算法求出,那么它的时间复杂度为O(c*(m+n)),c:为模式串的个数.m:为模式串的长度,n:为正文的长度,那 么这个复杂度就不再是线性了,我们学算法就是希望能把要解决的问题优化到极致,这不,AC自动机就派上用场了. 其实AC自动机就是Trie树的一个活用,活用点就是灌输了kmp的思想,从而再次把时间复杂度优

2018/2/22 每日一学 AC自动机

AC自动机=trie树+KMP. 代码如下:(自行理解) #include<cstdio> #include<string.h> #include<math.h> #include<queue> #include<algorithm> #define N 500006 using namespace std; char st[1000005]; char keyword[55]; int n,m; int next[N][26],cnt[N],f

[coci2012]覆盖字符串 AC自动机

给出一个长度为N的小写字母串,现在Mirko有M个若干长度为Li字符串.现在Mirko要用这M个字符串去覆盖给出的那个字符串的.覆盖时,必须保证:1.Mirko的字符串不能拆开,旋转:2.Mirko的字符串必须和给出的字符串的某一连续段完全一致才能覆盖,3.若干次覆盖可以部分重叠4.Mirko的字符串可以无限使用.求给出的字符串当中,有多少个字母是无法覆盖的. 小朋友们,作为一名长者,我认为我有必要向你们传授一些人生的经验~: 字符串的一堆函数,慎用慎用: 本人只因没有仔细认真,把strlen(

AC自动机算法详解

首先简要介绍一下AC自动机:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法之一.一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过.要搞懂AC自动机,先得有模式树(字典树)Trie和KMP模式匹配算法的基础知识.AC自动机算法分为3步:构造一棵Trie树,构造失败指针和模式匹配过程.     如果你对KMP算法和了解的话,应该知道KMP算法中的next函数(shift函数或者fail函数)是干

AC自动机:BZOJ 2434 阿狸的打字机

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1834  Solved: 1053[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽

POJ 3691 DNA repair AC自动机 + DP

题意:给你只包含‘A’,‘G’,‘T’,‘C’四个字母的n个模板串和1个文本串,问你文本串改变多少个字符就可以使得文本串中没有一个模板串 解题思路: 我们可以知道  dp[i][j] 为文本串到 第i 个字符  AC自动机状态为j的最少的变换次数(这里为什么要用AC自动机,因为end数组可以记录哪一个状态是结束的,而且处理以后可以知道那些后缀等于前缀--也就是不能到达,因为如果能够到达的话那么状态更新就会产生错误.),这样dp即可 解题代码: 1 // File Name: temp.cpp 2

Bzoj1195 [HNOI2006]最短母串 [AC自动机]

Time Limit: 10 Sec  Memory Limit: 32 MBSubmit: 1304  Solved: 439 Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串. Input 第一行是一个正整数n(n<=12),表示给定的字符串的个数.以下的n行,每行有一个全由大写字母组成的字符串.每个字符串的长度不超过50. Output 只有一行,为找到的最短的字符串T.在保证最短的前提下,如果

P3808 【模版】AC自动机(简单版)

题目背景 这是一道简单的AC自动机模版题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保证正确的基础上只有两组数据,请不要恶意提交. 题目描述 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. 输入输出格式 输入格式: 第一行一个n,表示模式串个数: 下面n行每行一个模式串: 下面一行一个文本串. 输出格式: 一个数表示答案 输入输出样例 输入样例#1: 2 a aa aa 输出样例#1: 2 说明 subtask1[50pts]:∑length(模式串)<=10^6,len

笔试算法题(45):AC自动机(Aho-Corasick Automation)

议题:AC自动机(Aho-Corasick Automation) 分析: 此算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一:一个常见的例子就是给定N个单词,给定包含M个字符的文章,要求确定多少个给定的单词在文章中出现过:AC自动机在匹配文本时不需要回溯,处理时间复杂度与pattern无关,仅是target的长度O(N):构建AC自动机的时间复杂度: 与KMP算法类似,AC自动机也是利用前一个匹配模式串失效之后得到的信息来确定下一个匹配的开始位置,从而避免回移主串的匹配指针:与KM