AC自动机专题

这几天跟着hh大神的博客刷AC自动机,http://www.notonlysuccess.com/index.php/aho-corasick-automaton/

模板题:

        Keywords Search 这题不说了,很裸的多模式串的匹配

#include <iostream>
#include <cstdio>

using namespace std; 
const int NODE=500010;
const int CH=26;
int chd[NODE][CH],word[NODE],fail[NODE],sz;

void Ins(char *s)
{
     int p=0;
    for(;*s;s++)     
    {
        int index=*s-'a';                 
        if(!chd[p][index])
        {
             memset(chd[sz],0,sizeof(chd[sz]));                  
             word[sz]=0;
             chd[p][index]=sz++;
        }
        p=chd[p][index];
    }
    word[p]++;
}
int Que[NODE];
void AC()
{
     int *s=Que,*e=Que;
     for(int i=0;i<CH;i++)
       if(chd[0][i])
       {
           *e++=chd[0][i];             
           fail[chd[0][i]]=0;
       }
     while(s!=e)  
     {
          for(int i=0;i<CH;i++)             
            if(chd[*s][i])
            {
                 int v=chd[*s][i];
                 fail[v]=chd[fail[*s]][i];              
                 *e++=v;
            }
            else
              chd[*s][i]=chd[fail[*s]][i];
            s++;  
     }
}
int solve(char *s)
{
    int sum=0,p=0;
    for(;*s;s++)
    {
        int index=*s-'a';        
        p=chd[p][index];            
        for(int tmp=p;tmp>0&&word[tmp]>=0;tmp=fail[tmp])
          sum+=word[tmp],word[tmp]=-1;
    }
    return sum;
}
char ss[1000010];
int main()
{
     int ca,cas=1,n;
     scanf("%d",&ca);
     while(ca--)
     {
           memset(chd[0],0,sizeof(chd[0]));
           fail[0]=0;
           sz=1;
           scanf("%d",&n);           
           for(int i=0;i<n;i++)
           {
                scanf("%s",ss);   
                Ins(ss);   
           }
           AC();
           scanf("%s",ss);
           printf("%d\n",solve(ss));
     }
     return 0;    
}
病毒侵袭  这题有多个主串,所以不能把匹配过的word[p]=-1

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
const int NODE=101100;
#define FF(i,a) for(int i=0;i<a;i++)
#define id(c) c
int chd[NODE][128],word[NODE],fail[NODE],sz;
char s1[500],s2[11000];
vector<int> ans;
void Ins(char *s,int val)
{
     int p=0;
     for(;*s;s++)
     {
           if(!chd[p][id(*s)])                
           {
                memset(chd[sz],0,sizeof(chd[sz]));                                     
                word[sz]=0;
                chd[p][id(*s)]=sz++;     
           }
           p=chd[p][id(*s)];
     }    
     word[p]=val;
}
int Que[NODE];
void AC()
{
     int *s=Que,*e=Que;
     FF(i,128) if(chd[0][i])
     {
          fail[chd[0][i]]=0;
          *e++=chd[0][i];         
     }     
     while(s!=e)
     {
          int p=*s++;
          FF(i,128)
            if(chd[p][i])
            {
                int k=chd[p][i];             
                fail[k]=chd[fail[p]][i];
                *e++=k;
            }
            else
              chd[p][i]=chd[fail[p]][i];
     }
}
int search(char *s)
{
     ans.clear();
     int p=0,sum=0;
     for(;*s;s++)
     {
         p=chd[p][*s];     
         for(int tmp=p;tmp>0;tmp=fail[tmp])
             if(word[tmp]) sum++,ans.push_back(word[tmp]);
     }
     return sum;
}
int main(int argc, char *argv[])
{
    int n,m,tot=0;
    while(scanf("%d",&n)==1)
    {
    getchar();
    memset(chd[0],0,sizeof(chd[0]));
    sz=1;
    for(int i=0;i<n;i++)
    {
        scanf("%s",s1);    
        Ins(s1,i+1);
    }
    AC();
    scanf("%d",&n);
    getchar();
     FF(i,n)
     {
       scanf("%s",s2);    
       int dd=search(s2);
       if(dd!=0)
       {
           tot++;     
           printf("web %d:",i+1);  
           sort(ans.begin(),ans.end());       
           for(int i=0;i<ans.size();i++)
             printf(" %d",ans[i]);
           printf("\n");  
       }
      }
      printf("total: %d\n",tot);
    }
    system("PAUSE");
    return EXIT_SUCCESS;
}


病毒持续侵袭中   http://acm.hdu.edu.cn/showproblem.php?pid=3065

   这题比较蛋疼,题意以为是单case,但是一直wa,改下就过了

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
const int NODE=500010;
#define FF(i,a) for(int i=0;i<a;i++)
#define id(c) c-'A'
int chd[NODE][26],word[NODE],fail[NODE],sz,ans[1100];
char s1[1100][550],s2[2000100];

void Ins(char *s,int val)
{
     int p=0;
     for(;*s;s++)
     {
           if(!chd[p][id(*s)])                
           {
                memset(chd[sz],0,sizeof(chd[sz]));                                     
                word[sz]=0;
                chd[p][id(*s)]=sz++;     
           }
           p=chd[p][id(*s)];
     }    
     word[p]=val;
}
int Que[NODE];
void AC()
{
     int *s=Que,*e=Que;
     FF(i,26) if(chd[0][i])
     {
          fail[chd[0][i]]=0;
          *e++=chd[0][i];         
     }     
     while(s!=e)
     {
          int p=*s++;
          FF(i,26)
            if(chd[p][i])
            {
                int k=chd[p][i];             
                fail[k]=chd[fail[p]][i];
                *e++=k;
            }
            else
              chd[p][i]=chd[fail[p]][i];
     }
}
void search(char *s)
{
     int p=0,sum=0;
     for(;*s;s++)
     {
         if(!isupper(*s)) {p=0;continue;}       
         p=chd[p][id(*s)];     
         for(int tmp=p;tmp>0;tmp=fail[tmp])
             if(word[tmp]) ans[word[tmp]]++;
     }
}
int main(int argc, char *argv[])
{
      int n;
      fail[0]=0;
      while(~scanf("%d",&n))
      {
      getchar();
      memset(chd[0],0,sizeof(chd[0]));
      memset(ans,0,sizeof(ans));
      sz=1;
      for(int i=0;i<n;i++)
       {
         scanf("%s",s1[i]);    
         Ins(s1[i],i+1);
       }
       AC();
       scanf("%s",s2);
       search(s2);
       FF(i,n)
         if(ans[i+1]) printf("%s: %d\n",s1[i],ans[i+1]);
       }
      system("PAUSE");
      return EXIT_SUCCESS;
}

DNA Sequence   http://poj.org/problem?id=2778

 这次终于弄的有点明白了,

http://blog.henix.info/blog/poj-2778-aho-corasick-dp.html ,

http://hi.baidu.com/%D2%D5%C1%D6010/blog/item/6db06ccf0a3b440993457e7b.html

它讲的不错,把root节点当做所有可能的安全节点,然后构造状态转移矩阵,最多有101中状态,然后如果当前节点+A(或者T,C,G)的状态pre,能够转移的最长公共前缀的状态cur且cur为安全状态,则map[pre][cur]++;

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
#define mm(a,what) memset(a,what,sizeof(a))
#define ff(i,a) for(int i=0;i<a;i++)

long long map[101][101],map1[101][101],map2[101][101],n;
int chd[110][4],fail[110],sz,word[110];
char s[11];
int hash[128];
void Ins(char *s)
{
     int p=0;
    for(;*s;s++)
    {
       if(!chd[p][hash[*s]])           
       {
           mm(chd[sz],0);                                
           word[sz]=0;
           chd[p][hash[*s]]=sz++;
       }
       p=chd[p][hash[*s]];
    }  
    word[p]=1;   
}
int Que[1100];
void ac()
{
     int *s=Que,*e=Que;
    for(int i=0;i<4;i++)
      if(chd[0][i])    
      {
          *e++=chd[0][i];                 
          fail[chd[0][i]]=0;
      } 
     while(s!=e)
     {
          int p=*s++;
          for(int i=0;i<4;i++)
            if(chd[p][i])           
            {
                 fail[chd[p][i]]=chd[fail[p]][i];
                 *e++=chd[p][i];
                 word[chd[p][i]]|=word[fail[chd[p][i]]];                    
            }
            else chd[p][i]=chd[fail[p]][i];
     } 
}
void loop()
{
     for(int i=0;i<=sz;i++)
      if(!word[i])
      {
          for(int j=0;j<4;j++)
            if(!word[chd[i][j]]) map[i][chd[i][j]]++;                  
      }
}
void solve(long long s){
    int i,j,k;
    if(s==1)
        for(i=0;i<sz;i++)
            for(j=0;j<sz;j++)
                map1[i][j]=map[i][j];
    else
    {
        solve(s/2);
        for(i=0;i<sz;i++)
            for(j=0;j<sz;j++)
                for(k=0,map2[i][j]=0;k<sz;k++)
                {
                    map2[i][j]+=map1[i][k]*map1[k][j];
                    map2[i][j]%=100000;
                }
        if(s/2*2==s)
        {
            for(i=0;i<sz;i++)
                for(j=0;j<sz;j++)
                    map1[i][j]=map2[i][j];
        }
        else
        {
            for(i=0;i<sz;i++)
                for(j=0;j<sz;j++)
                    for(k=0,map1[i][j]=0;k<sz;k++)
                    {
                        map1[i][j]+=map2[i][k]*map[k][j];
                        map1[i][j]%=100000;
                    }
        }
    }
}
int main()
{
    int i,j,k,m;
    char c[20];
    cin>>m>>n;
    hash['A']=0;
    hash['C']=1;
    hash['T']=2;
    hash['G']=3;
    sz=1;
    mm(chd[0],0);
    for(i=1;i<=m;i++)
    {
        cin>>s;
        Ins(s);
    }
    ac();
    //getchar();
    loop();
   /* cout<<sz<<endl;
    for(int i=0;i<=sz;i++)
    {
       for(int j=0;j<=sz;j++)
        cout<<map[i][j]<<" ";
       cout<<endl; 
    }*/ 
    solve(n);
    long long ans=0;
    for(i=0;i<sz;i++)
    {
        ans+=map1[0][i];
        ans%=100000;
    }
    cout<<ans<<endl;
  // system("pause");
    return 0;    
}


考研路茫茫-单词情节 http://acm.hdu.edu.cn/showproblem.php?pid=2243这题我快无语了,不知道怎么就Runtime Error(ACCESS_VIOLATION)了,无语了找了块一天了,以后再找吧,用ac自动机建矩阵肯定对的,以后再检查吧

#include <cstdlib>
#include <iostream>

using namespace std;
#define ff(i,a) for(int i=0;i<a;i++)
#define mm(a,what) memset(a,what,sizeof(what))
#define  ll unsigned __int64

const int NODE=500;
int chd[NODE][26],word[NODE],sz,fail[NODE];

void Ins(char *s)
{
    int p=0;
    for(;*s;s++)
    {
         int id=*s-'a'; 
         if(!chd[p][id])            
         {
              mm(chd[sz],0);
              word[sz]=0;
              chd[p][id]=sz++;
         }
         p=chd[p][id];
    }     
    word[p]=1;
}
int Que[500];
void ac()
{
     int *s=Que,*e=Que;
     ff(i,26) if(chd[0][i])
       *e++=chd[0][i],fail[chd[0][i]]=0;
     while(s!=e)
     {
         int p=*s++;
         ff(i,26) if(chd[p][i])           
         {
            *e++=chd[p][i];         
            fail[chd[p][i]]=chd[fail[chd[p][i]]][i];
            word[chd[p][i]]|=word[chd[fail[chd[p][i]]][i]];
         }
         else
            chd[p][i]=chd[fail[chd[p][i]]][i];
     }  
}
ll a1[100][100],a2[100][100],ans[100][100];
char s[400];
struct Matrix{    
    ll matrix[100][100];    
    Matrix operator*(const Matrix & m)
    {
        Matrix ans;    
        int i,j,k;
        for (i = 0;i < sz;i++)    
            for (j =0;j < sz ;j ++)       
                    for (ans.matrix[i][j]=0, k = 0;k < sz;k++)    
                    {    
                        ans.matrix[i][j]+=matrix[i][k]*m.matrix[k][j];    
                    }    
                return ans;    
    }    
}aa,unit;
void build()
{
     ff(i,sz)
     ff(j,sz) aa.matrix[i][j]=0;
     ff(i,sz)
      if(!word[i])
      ff(j,26)
        if(!word[chd[i][j]]) aa.matrix[i][chd[i][j]]++;   
}
Matrix pow(Matrix a,ll n)
{
    Matrix p = a,ans = unit;    
    while(n)
    {    
        if (n&1)     
            ans = ans*p;    
        p = p*p;    
        n>>=1;    
    }    
    return ans;    
}  
Matrix getNum(Matrix a,ll n)
{
    if(n==1)  return a;
    Matrix tmp=getNum(a,n/2),ans;   
    if(n&1)
    {
          Matrix tmp1=pow(a,n/2+1);       
          Matrix tmp2=tmp1*tmp;
          ff(i,sz) ff(j,sz)
            ans.matrix[i][j]=tmp.matrix[i][j]+tmp1.matrix[i][j]+tmp2.matrix[i][j];
    }
    else
    {
          Matrix tmp1=pow(a,n/2);  
          Matrix tmp2=tmp1*tmp;
          ff(i,sz) ff(j,sz)
            ans.matrix[i][j]=tmp.matrix[i][j]+tmp2.matrix[i][j];  
    }
    return ans;
}
int main(int argc, char *argv[])
{
    int n;
    ll L;
    memset(unit.matrix,0,sizeof(unit.matrix));
    ff(i,100) unit.matrix[i][i]=1;
    while(cin>>n>>L)
    {
         mm(chd[0],0);           
         sz=1;
         ff(i,n) cin>>s,Ins(s);
         ac();
         build();
        Matrix ans=getNum(aa,L);
        ll ans1=0,ans2=0;
        ff(i,sz)
          ans1+=ans.matrix[0][i];
        memset(aa.matrix,0,sizeof(aa.matrix));  
        aa.matrix[0][0]=26;
        ans=getNum(aa,L);
        ans2=ans.matrix[0][0]-ans1;
        if(ans2<0) ans2=ans2+((ll)1<<63)+((ll)1<<63);
        cout<<ans2<<endl;  
    }
   // system("PAUSE");
    return EXIT_SUCCESS;
}

wireless password http://acm.hdu.edu.cn/showproblem.php?pid=2825 基于ac自动机的状态压缩的dp,建trie树的总的节点数为100,那么可以再以这100个节点为状态转移,每个节点记录当前节点所能够覆盖的最多的单词数,应为最多只有10个,所以可以用状态压缩,dp[len][state][last] 表示长度为len的覆盖为state的且最后一个状态为last节点。时间复查度O(25*2^10*100)

#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;
#define ff(i,a) for(int i=0;i<a;i++)
#define mm(a,what) memset(a,what,sizeof(a))

const int N=27;
const int M=12;
const int NODE=110;
const int MOD=20090717;
int dp[N][1<<10][NODE],chd[NODE][26],fail[NODE],word[NODE],sz,n,m,k;
int bits[1<<10];
void init()
{
   bits[0]=0;     
   for(int i=1;i<(1<<10);i++)
     bits[i]=bits[i>>1]+(1&i);
}
void Ins(char *s,int val)
{
    int p=0;
    for(;*s;s++)
    {
         int id=*s-'a';       
         if(!chd[p][id])            
         {
             mm(chd[sz],0);
             word[sz]=0;
             chd[p][id]=sz++;
         }
         p=chd[p][id];
    }     
    word[p]=1<<val;
}
int Que[NODE];
void ac()
{
     int *s=Que,*e=Que;
     ff(i,26) if(chd[0][i])
     {
          *e++=chd[0][i];         
          fail[chd[0][i]]=0;
     }
     while(s!=e)
     {
         int p=*s++;
         ff(i,26) if(chd[p][i])           
         {
            *e++=chd[p][i];         
            fail[chd[p][i]]=chd[fail[p]][i];
            word[chd[p][i]]|=word[fail[chd[p][i]]];
         }
         else 
           chd[p][i]=chd[fail[p]][i];
     }
}
void solve()
{
    memset(dp,0,sizeof(dp));     
    dp[0][0][0]=1;
    int full=1<<m;
    for(int i=0;i<=n;i++)
    for(int j=0;j<full;j++)
    for(int d=0;d<sz;d++)
    {
         if(!dp[i][j][d])   continue;
         ff(d1,26)
         {
            int state=word[chd[d][d1]];      
            dp[i+1][state|j][chd[d][d1]]=(dp[i+1][state|j][chd[d][d1]]+dp[i][j][d])%MOD;
         }
    }
    int ans=0;
  //  ff(i,n+1) cout<<dp[i][0][0]<<endl;
    for(int i=0;i<(1<<m);i++)
    if(bits[i]>=k)
     for(int j=0;j<sz;j++) ans+=dp[n][i][j],ans%=MOD; 
    printf("%d\n",ans);  
}
char s[20];

int main(int argc, char *argv[])
{
    init();
    while(scanf("%d %d %d",&n,&m,&k)==3)
    {
        getchar();            
        if(!n&&!m&&!k) break;
        mm(chd[0],0);
        sz=1;
        ff(i,m)
        {
            scanf("%s",s);       
            Ins(s,i);
        }
        ac();
        solve();
    }
    //system("PAUSE");
    return EXIT_SUCCESS;
}

http://acm.uestc.edu.cn/problem.php?pid=1633

   给出最多为20个模式串,每个串长最多为15,然后用最多1000个字符包含最多多少个模式串。dp[len][state]表示在state节点结尾的长度为len的个数

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
#define ff(i,a) for(int i=0;i<a;i++)
#define mm(a,what) memset(a,what,sizeof(a))

const int NODE=410;
const int Maxlen=1100;
int chd[NODE][3],word[NODE],fail[NODE],sz;
long long dp[Maxlen][NODE];
int n,m;

void Ins(char *s,int v)
{
    int p=0;
    for(;*s;s++)
    {
        int id=*s-'A';
        if(!chd[p][id])            
        {
           mm(chd[sz],0);                           
           word[sz]=0;
           chd[p][id]=sz++;
        }
        p=chd[p][id];
    }     
    word[p]+=v;
}
int Que[NODE];
void ac()
{
     int *s=Que,*e=Que;
     ff(i,3) if(chd[0][i])     
     {
         *e++=chd[0][i];         
         fail[chd[0][i]]=0;
     }
     while(s!=e)
     {
         int p=*s++;           
         ff(i,3) if(chd[p][i])
         {
              *e++=chd[p][i];         
              fail[chd[p][i]]=chd[fail[p]][i];
              word[chd[p][i]]+=word[fail[chd[p][i]]];
         }
         else
          chd[p][i]=chd[fail[p]][i];
     }
}
void solve()
{
    mm(dp,-1);
    dp[0][0]=0;
    long long Max=0;
    ff(i,n+1)ff(j,sz)
    {
        if(dp[i][j]==-1)   continue;          
        ff(k,3)             
        {
           int next=chd[j][k];
           if(dp[i+1][next]<dp[i][j]+word[next])
           {
               dp[i+1][next]=dp[i][j]+word[next];
           }
        } 
        if(dp[i][j]>Max)               
           Max=dp[i][j];                              
    }
  printf("%lld\n",Max);
}
char s[110][20];
int val[110];

int main(int argc, char *argv[])
{
     while(scanf("%d %d",&m,&n)==2)
     {           
         getchar();
         mm(chd[0],0);
         sz=1;
         ff(i,m) 
             scanf("%s",s[i]);        
         ff(i,m) Ins(s[i],1);    
         ac();
         solve();
     }
    //system("PAUSE");
    return EXIT_SUCCESS;
}

Ring    http://acm.hdu.edu.cn/showproblem.php?pid=2296

  这题输入输出太蛋疼了,卡了我两天的时间,无语了大哭

#include <cstdlib>
#include <iostream>
#include <String>
#include <cstring>

using namespace std;
#define ff(i,a) for(int i=0;i<a;i++)
#define mm(a,what) memset(a,what,sizeof(a))

const int NODE=1100;
const int Maxlen=55;
int chd[NODE][26],fail[NODE],sz;
__int64 word[NODE];
__int64 dp[Maxlen][NODE];
int n,m;
struct Node
{
   string s;       
}node[Maxlen][NODE];

void Ins(char *s,__int64 v)
{
    int p=0;
    for(;*s;s++)
    {
        int id=*s-'a';
        if(!chd[p][id])            
        {
           mm(chd[sz],0);                           
           word[sz]=0;
           chd[p][id]=sz++;
        }
        p=chd[p][id];
    }     
    word[p]=v;
}
int Que[NODE];
void ac()
{
     int *s=Que,*e=Que;
     ff(i,26) if(chd[0][i])     
     {
         *e++=chd[0][i];         
         fail[chd[0][i]]=0;
     }
     while(s!=e)
     {
         int p=*s++;           
         ff(i,26) if(chd[p][i])
         {
              *e++=chd[p][i];         
              fail[chd[p][i]]=chd[fail[p]][i];
              word[chd[p][i]]+=word[fail[chd[p][i]]];
         }
         else
          chd[p][i]=chd[fail[p]][i];
     }
}
void solve()
{
    mm(dp,-1);
    dp[0][0]=0;
    node[0][0].s="";
    string ans="";
    __int64 Max=0;
    ff(i,n+1)ff(j,sz)
    {
        if(dp[i][j]==-1)   continue;          
        ff(k,26)             
        {
           int next=chd[j][k];
           if(dp[i+1][next]<dp[i][j]+word[next])
           {
               dp[i+1][next]=dp[i][j]+(__int64)word[next];
               node[i+1][next].s=node[i][j].s+(char)(k+'a');
           }
           else if(dp[i+1][next]==dp[i][j]+word[next])
           {
                 string ss=node[i][j].s+(char)(k+'a');
                 if(node[i+1][next].s.length()>ss.length())     
                   node[i+1][next].s=ss;
                 else if(node[i+1][next].s.length()==ss.length()&&node[i+1][next].s>ss)  
                   node[i+1][next].s=ss;
           }
        } 
        if(dp[i][j]>Max)               
           Max=dp[i][j],ans=node[i][j].s;                               
        else if(dp[i][j]==Max)   
        {
            if(ans.length()>node[i][j].s.length())     
               ans=node[i][j].s;
            else if(ans.length()==node[i][j].s.length()&&ans>node[i][j].s)   
              ans=node[i][j].s;
        }
        
    }
   cout<<ans<<endl;
}
char s[1100][150];
__int64 val[110];
int main(int argc, char *argv[])
{
    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
         scanf("%d %d",&n,&m);           
         getchar();
         mm(chd[0],0);
         sz=1;
         ff(i,m) 
             scanf("%s",s[i]);        
         ff(i,m) 
             scanf("%I64d",&val[i]);        
         ff(i,m) Ins(s[i],val[i]);    
         ac();
         solve();
    }
   // system("PAUSE");
    return EXIT_SUCCESS;
}

DNA repair   http://acm.hdu.edu.cn/showproblem.php?pid=2457  这题很简单,跟上一题状态表示一样,dp[len][state]表示长度len到达state状态所要修改的最小值,不能走到尾节点,太坑了这题,半个小时敲完代码,结果一个小bug调试了我两个小时

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define mm(a,what) memset(a,what,sizeof(a))
#define ff(i,a) for(int i=0;i<a;i++)

const int NODE=1010;
const int maxlen=1010;
const int ch=4;
const int INF=99999999;
int chd[NODE][ch],word[NODE],fail[NODE],sz;
int sw[128],dp[maxlen][NODE];
char ss[maxlen],ss1[100];

void Ins(char *s1)
{
     int p=0;
     for(;*s1;s1++)
     {
         int id=sw[*s1];
         if(!chd[p][id])
         {
             mm(chd[sz],0);
             word[sz]=0;
             chd[p][id]=sz++;
         }
         p=chd[p][id];
     }
     word[p]=1;
}
int Que[NODE];
void ac()
{
      int *s=Que,*e=Que;
      ff(i,ch) if(chd[0][i])
      {
           *e++=chd[0][i];
           fail[chd[0][i]]=0;
      }
      while(s!=e)
      {
           int p=*s++;
           ff(i,ch) if(chd[p][i])
           {
               *e++=chd[p][i];
               fail[chd[p][i]]=chd[fail[p]][i];
               word[chd[p][i]]|=word[fail[chd[p][i]]];
           }
           else
              chd[p][i]=chd[fail[p]][i];
      }
}
void solve()
{
     int len=strlen(ss);
     ff(i,len+1) ff(j,sz) dp[i][j]=INF;
     dp[0][0]=0;
     int ans=INF;
     ff(i,len+1) ff(j,sz)
     {
          if(dp[i][j]==INF) continue;
          ff(k,ch)
          if(!word[chd[j][k]])
          {
              int next=chd[j][k],tmp;
              if(sw[ss[i]]==k) tmp=dp[i][j];
              else tmp=dp[i][j]+1;
              dp[i+1][next]=min(dp[i+1][next],tmp);
          }
     }
     ff(i,sz) ans=min(ans,dp[len][i]);
     if(ans!=INF)
         printf("%d\n",ans);
     else printf("-1\n");
}
int main(int argc, char *argv[])
{
    sw['A']=0;
    sw['T']=1;
    sw['C']=2;
    sw['G']=3;
    int n,ca=1;
    while(scanf("%d",&n)==1&&n)
    {
          sz=1;
          mm(chd[0],0);
         // getchar();
          ff(i,n)
          {
                  scanf("%s",ss1);
                  Ins(ss1);
          }
          ac();
          scanf("%s",ss);
          printf("Case %d: ",ca++);
          solve();
    }
    //system("PAUSE");
    return EXIT_SUCCESS;
}

Searching the string     http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3441

    这个应该不算是dp吧,应该说的是贪心,建立ac自动机,如果当前匹配的单词和上次有重复的话就舍弃

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
#define ff(i,a) for(int i=0;i<a;i++)
#define mm(a,what) memset(a,what,sizeof(a))
const int NODE=1000010;
const int ch=26;

int chd[600010][ch],word[NODE],fail[NODE],sz;
char txt[NODE],ss[NODE];
int ans[NODE][2],last[NODE],len[NODE],pos[100010],n,id[100010];
int flag[NODE];
int Ins(char *s)
{
     int p=0;
     for(;*s;s++)
     {
          int id=*s-'a';
          if(!chd[p][id])            
          {
               mm(chd[sz],0);                           
               word[sz]=0;
               flag[sz]=0;
               ans[sz][0]=ans[sz][1]=0;
               last[sz]=-1;
               chd[p][id]=sz++;
          }
          p=chd[p][id];
     }    
     word[p]=1;
     flag[p]=1;
     len[p]=strlen(ss);
     return p;
}
int Que[NODE];
void ac()
{
     int *s=Que,*e=Que;
     ff(i,26) if(chd[0][i])
     {
         *e++=chd[0][i];     
         fail[chd[0][i]]=0;
     }
     while(s!=e)
     {
         int p=*s++;
         ff(i,26) if(chd[p][i])           
         {
              *e++=chd[p][i];         
              fail[chd[p][i]]=chd[fail[p]][i];
              flag[chd[p][i]]|=flag[fail[chd[p][i]]];
         }
         else
           chd[p][i]=chd[fail[p]][i];;
     }
}
void search()
{
      int len1=strlen(txt),p=0;     
      ff(i,len1) 
      {
           p=chd[p][txt[i]-'a'];
           for(int tmp=p;tmp&&flag[tmp];tmp=fail[tmp])
             if(word[tmp])
             {
                  ans[tmp][0]++;
                   if((i-last[tmp])>= len[tmp])       
                    ans[tmp][1]++,last[tmp]=i;
             }
      }
}
int main(int argc, char *argv[])
{
    int ca=1;
     while(scanf("%s",txt)==1)
     {
          getchar();                    
          scanf("%d",&n);                         
          mm(chd[0],0);
          sz=1;
          ff(i,n)
          {
               scanf("%d %s",&id[i],ss);       
               pos[i]=Ins(ss);
          }
          ac();
          search();
         // ff(i,sz) cout<<ans[i][0]<<" "<<ans[i][1]<<endl;
          printf("Case %d\n",ca++);
          ff(i,n)
            printf("%d\n",ans[pos[i]][id[i]]);
          puts("");  
     }
    //system("PAUSE");
    return EXIT_SUCCESS;
}

Lost's revenge     http://acm.hdu.edu.cn/showproblem.php?pid=3341

 这题不愧为神题呀,要非常优化的代码才能过,首先主串最多长40,正常用dp可以dp[40][40][40][40][state]来表示用了A T C G 以状态state结尾的最多的个数,这样肯定超内存,用状态压缩dp[dd][state] ,dd为压缩的状态,他有4位,每位的权值为bits[0] bits[1] bits[2] bits[3],这样时间复查度 O(10^4*500*4)

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
#define ff(i,a) for(int i=0;i<a;i++)
#define mm(a,what) memset(a,what,sizeof(a))

const int NODE=550;
int chd[NODE][4],fail[NODE],word[NODE],sz,sw[128];
int lim[4],Limit,n,ans;
void Ins(char *s)
{
     int p=0;
     for(;*s;s++)
     {
          int id=sw[*s];            
          if(!chd[p][id])
          {
             mm(chd[sz],0);               
             word[sz]=0;
             chd[p][id]=sz++;
          }
          p=chd[p][id];
     }     
     word[p]++;
}
int Que[NODE];
void ac()
{
     int *s=Que,*e=Que;
     ff(i,4) if(chd[0][i])
     {
          *e++=chd[0][i];        
          fail[chd[0][i]]=0;
     }
     while(s!=e)
     {
          int p=*s++;
          ff(i,4) if(chd[p][i])           
          {
               *e++=chd[p][i];
               fail[chd[p][i]]=chd[fail[p]][i];
               word[chd[p][i]]+=word[fail[chd[p][i]]];
          }
          else
             chd[p][i]=chd[fail[p]][i];
     }
}
int dp[29999][550],bits[4];

void solve()
{
     mm(dp,-1);    
     dp[0][0]=0;
     ff(i,lim[0]+1)
     ff(j,lim[1]+1)
     ff(k,lim[2]+1)
     ff(r,lim[3]+1)
     {
          int base=i+j*bits[1]+k*bits[2]+r*bits[3];
          ff(s1,sz)
          {
             if(dp[base][s1]==-1) continue;
             if(i!=lim[0])
             {
                  dp[base+1][chd[s1][0]]=max(dp[base+1][chd[s1][0]],dp[base][s1]+word[chd[s1][0]]);
             }
             if(j!=lim[1])
             {
                  int next=base+bits[1];             
                  dp[next][chd[s1][1]]=max(dp[next][chd[s1][1]],dp[base][s1]+word[chd[s1][1]]);
             }
             if(k!=lim[2])
             {
                  int next=base+bits[2];            
                  dp[next][chd[s1][2]]=max( dp[next][chd[s1][2]],dp[base][s1]+word[chd[s1][2]]);
             }
             if(r!=lim[3])
             {
                  int next=base+bits[3];             
                  dp[next][chd[s1][3]]=max(dp[next][chd[s1][3]],dp[base][s1]+word[chd[s1][3]]);
             }
           //  ans=max(ans,dp[base][s1]);
          }
     }
     for(int i=0;i<sz;i++)
       ans=max(ans,dp[lim[0]*bits[0]+lim[1]*bits[1]+lim[2]*bits[2]+lim[3]*bits[3]][i]);
}
char ss[55];
int main(int argc, char *argv[])
{
    sw['A']=0;
    sw['T']=1;
    sw['C']=2;
    sw['G']=3;
    int ca=1;
    while(scanf("%d",&n)==1&&n)
    {
          fail[0]=0;                     
          mm(chd[0],0);
          sz=1;                     
          ff(i,n) scanf("%s",ss),Ins(ss);
          ac();
          scanf("%s",ss);
          Limit=strlen(ss);
          mm(lim,0);
          ff(i,Limit) lim[sw[ss[i]]]++;
          bits[0]=1;
          bits[1]=(lim[0]+1);
          bits[2]=(lim[0]+1)*(lim[1]+1);
          bits[3]=(lim[0]+1)*(lim[1]+1)*(lim[2]+1);
          ans=0;
          solve();
          printf("Case %d: %d\n",ca++,ans);
    }
   // system("PAUSE");
    return EXIT_SUCCESS;
}

空罐 Cans    http://zerojudge.tw/ShowProblem?problemid=b179

   这题NB呀,做完这题后高中生在我心中的形象瞬间高大了,这么好的题!感谢hh神牛提供思路,不然这题我真想不出来,dp[len][state] 表示长度为len并且尾节点在state处的

个数,用fail指针用作去掉头节点作为状态转移,chd[][]用作从后加一个字符,用滚动数组优化

    我们因为是主要关注的是一个串的末尾的情况,当我们要在开头删掉一个串时,如果这个串串长比当前根节点到改点的距离要大的话,删掉串的首字母其实是该节点是不移动的,否则的话该节点要跟着fail指针走,fail指针节点的高度肯定要比该节点高度要小,所以这样就至少删掉了一个字母

  考虑增长的情况,在末尾添加一个字符,可能会使他生病,否则的话跟着chd走就行了

#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
#define ff(i,a) for(int i=0;i<a;i++)
#define mm(a,what) memset(a,what,sizeof(a))

const int NODE=1510;
const int maxlen=110;
const int MODE=10007;
int chd[NODE][4],fail[NODE],word[NODE],sz,dp[2][maxlen][NODE],n,P;
int dis[NODE];
char native[110],ss[110];

void Ins()
{
    int p=0,len=strlen(ss);
    for(int i=0;i<len;i++)
    {
        int id=ss[i]-'a';
        if(!chd[p][id])
        {
           mm(chd[sz],0);               
           word[sz]=0;
           chd[p][id]=sz++;
        }
        p=chd[p][id];
        dis[p]=i+1;
    }     
    word[p]=1;
}
int Que[NODE];
void ac()
{
     int *s=Que,*e=Que;
     ff(i,4) if(chd[0][i])
     {
        *e++=chd[0][i];        
        fail[chd[0][i]]=0;
     }
     while(s!=e)
     {
         int p=*s++;           
         ff(i,4) if(chd[p][i])
         {
             *e++=chd[p][i];        
             fail[chd[p][i]]=chd[fail[p]][i];
             word[chd[p][i]]|=word[fail[chd[p][i]]];
         }
         else
           chd[p][i]=chd[fail[p]][i];
     }
}
void solve()
{
    int ans1=0,ans2=0,p=0,len=strlen(native);     
    for(int i=0;native[i];i++) p=chd[p][native[i]-'a'];
    if(word[p]) { printf("0 1\n");return; }
    mm(dp,0); 
    dp[0][len][p]=1;
    int Maxlen=len,now=0;
    while(P--) 
    {
       mm(dp[now^1],0);         
       for(int i=1;i<=Maxlen;i++)
       for(int j=0;j<sz;j++)
       {
          if(!dp[now][i][j]) continue;
          if(i==1) 
             ans1=(ans1+dp[now][i][j])%MODE;
          else if(i<=dis[j])   
             dp[now^1][i-1][fail[j]]=(dp[now^1][i-1][fail[j]]+dp[now][i][j])%MODE;
          else
             dp[now^1][i-1][j]=(dp[now^1][i-1][j]+dp[now][i][j])%MODE;   
       }
       for(int i=1;i<=Maxlen;i++)
       for(int j=0;j<sz;j++)
       for(int k=0;k<4;k++)
         if(!word[chd[j][k]])
            dp[now^1][i+1][chd[j][k]]=(dp[now^1][i+1][chd[j][k]]+dp[now][i][j])%MODE;
         else ans2=(ans2+dp[now][i][j])%MODE;  
         now=1-now;
         Maxlen++;
    }
    printf("%d %d\n",ans1,ans2);
}

int main(int argc, char *argv[])
{
    while(scanf("%s",native)==1)
    {  
         scanf("%d %d",&P,&n);
         mm(chd[0],0);
         sz=1;
         dis[0]=0;
         ff(i,n) scanf("%s",ss),Ins();
         ac();
         solve();
    }
    //system("PAUSE");
    return EXIT_SUCCESS;
}

Resource Archiver   http://acm.hdu.edu.cn/showproblem.php?pid=3247

    最后一题了,窃喜以下,这题开始把memset(a,what,sizeof(a))写成了 memset(a,what,sizeof(waht)) re了一个上午,之前都犯过这样的错,白痴呀!然后MLE,然后TLE,各种蛋疼……题意是给出最多10个串Resource,每个串长最多为1000,在给出最多1000个病毒串,最长为50000,求一个串包含所有的Resource但是不含任何病毒码的最小长度;

    空间优化:因为Resource的串长最多为1000,所以长度超过1000个病毒串不可能污染Resource,那么就不考虑这个病毒串,然后建立ac自动机空间复查度由O(50000*1000*2)优化到O(1000*1000*2)

   时间优化:在ac自动机上求Resource之间的最短路 dp[state][end] 这样 0<=end<10 ,缩短了状态数量 由O(2^10*10*10)

很难看的代码

#include <cstdlib>
#include <iostream>
#include <cstdio>

using namespace std;
#define ff(i,a) for(int i=0;i<a;i++)
#define mm(a,what) memset(a,what,sizeof(a))
const int NODE=1000010;

int chd[NODE][2],word[NODE],fail[NODE],sz;
int dp[1<<10][11];
int n,m,limit;
char ss[1010],ss1[50110];

int Ins(char *s,int val)
{
   int p=0;
   for(;*s;s++)
   {
       int id=*s-'0';    
       if(!chd[p][id])            
       {
           mm(chd[sz],0);                            
           word[sz]=0;
           chd[p][id]=sz++;
       }
       p=chd[p][id];
   }     
   if(val==-1) word[p]=val;
   else word[p]=1<<val;
   return p;
}
int Que[NODE];
bool vis[NODE];
int dis[NODE];
int end[11],height[11];
void ac()
{
    int *s=Que,*e=Que;
    ff(i,2) if(chd[0][i]) 
    {
        fail[chd[0][i]]=0;
        *e++=chd[0][i];        
    }
    while(s!=e)
    {
        int p=*s++;
        ff(i,2) if(chd[p][i])          
        {
            *e++=chd[p][i];
            fail[chd[p][i]]=chd[fail[p]][i];
            if(word[fail[chd[p][i]]]==-1) word[chd[p][i]]=-1;
            else if(word[chd[p][i]]!=-1) word[chd[p][i]]|=word[fail[chd[p][i]]];
        }
        else chd[p][i]=chd[fail[p]][i];
    }
}
void find_path(int st)
{
     int *s=Que,*e=Que;
     *e++=st;
     mm(vis,0); 
     mm(dis,-1);
     if(word[st]==-1) return;
     vis[st]=1;
     dis[st]=0;
     while(s!=e)
     {
         int p=*s++;       
         ff(i,2) if(!vis[chd[p][i]])
         {
             vis[chd[p][i]]=1;
              if(word[chd[p][i]]!=-1) dis[chd[p][i]]=dis[p]+1,*e++=chd[p][i];
         }
     }
}
int g[11][11];
int min1(int a,int b)
{
     if(a==-1) return b;
     return a<b?a:b;    
}
void solve()
{
    int now=0,ans=1,Max=1<<n;
    bool find=0;
    ff(i,n) 
    {
        find_path(end[i]);
        ff(j,n) g[i][j]=dis[end[j]];
    }
    mm(dp,-1);
    ff(i,n)
     if(word[end[i]]!=-1)  dp[word[end[i]]][i]=height[i];
    ff(i,Max) ff(j,n)
         if(dp[i][j]!=-1)
         {
             ff(k,n)
              if((i&(1<<k))==0&&g[i][j]!=-1) dp[i|(1<<k)][k]=min1(dp[i|(1<<k)][k],dp[i][j]+g[j][k]);
         }
    ans=-1;     
    ff(i,n) if(dp[Max-1][i]!=-1) ans=min1(ans,dp[Max-1][i]);
    printf("%d\n",ans);
}
int main(int argc, char *argv[])
{
    int Maxlen;
    while(scanf("%d %d",&n,&m)==2&&(n||m))
    {
        getchar();            
        mm(chd[0],0);
        sz=1;  
        Maxlen=0;                  
        ff(i,n) 
        {
           scanf("%s",ss),end[i]=Ins(ss,i);
           height[i]=strlen(ss);
           int len=strlen(ss);
           Maxlen=max(Maxlen,len);
        }
        limit=sz;
        ff(i,m) 
        {
           scanf("%s",ss1);
           if(strlen(ss1)<=Maxlen) Ins(ss1,-1);
        }
        ac();
        solve();
    }
    //system("PAUSE");
    return EXIT_SUCCESS;
}


你可能感兴趣的:(AC自动机专题)