推荐博客:http://www.cnblogs.com/kuangbin/p/3164106.html AC自动机小结
https://blog.csdn.net/creatorx/article/details/71100840 AC自动机最详细的解释
2006年国家集训队论文:Trie图的构建、活用与改进 王赟
1.(HDOJ2222)http://acm.hdu.edu.cn/showproblem.php?pid=2222
题意:求目标串中出现了几个模式串。
分析:AC自动机模板题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<queue> 6 using namespace std; 7 const int maxn=500010; 8 9 struct Trie 10 { 11 int nxt[maxn][26],fail[maxn],end[maxn]; //nxt为字典树, fail相当于kmp中的nxt数组, end对单词结尾做标记 12 int root,L; //L相当于字典树中的sz ,root为根节点(即;0) 13 int newnode() //初相当于始化一个字典树上的节点 14 { 15 for ( int i=0;i<26;i++ ) nxt[L][i]=-1; 16 end[L++]=0; 17 return L-1; 18 } 19 void init() 20 { 21 L=0; 22 root=newnode(); 23 } 24 void insert(char buf[]) //大致于字典树插入过程相同 25 { 26 int len=strlen(buf); 27 int now=root; 28 for ( int i=0;i<len;i++ ) 29 { 30 int x=buf[i]-'a'; 31 if ( nxt[now][x]==-1 ) nxt[now][x]=newnode(); 32 now=nxt[now][x]; 33 } 34 end[now]++; //在单词结尾处做标记 35 } 36 void build() //相当于kmp的操作 37 { 38 queue<int>que; 39 fail[root]=root; //根节点初始化为0(即其本身) 40 for (int i=0;i<26;i++ ) 41 { 42 if ( nxt[root][i]==-1 ) nxt[root][i]=root; 43 else //Trie中已经构建的节点 44 { 45 int x=nxt[root][i]; 46 fail[x]=root; 47 que.push(x); 48 } 49 } 50 while ( !que.empty() ) 51 { 52 int now=que.front(); 53 que.pop(); 54 for ( int i=0;i<26;i++ ) 55 { 56 if ( nxt[now][i]==-1 ) //无后继点 57 nxt[now][i]=nxt[fail[now]][i];//类似于kmp中求nxt数组一样 58 else //存在下一个节点 59 { 60 int x=nxt[now][i]; 61 fail[x]=nxt[fail[now]][i];//失配指针指向他父节点的失配指针的下一个相同字符处 62 que.push(x); 63 } 64 } 65 } 66 } 67 int query(char buf[]) //相当于字典树中的访问操作 68 { 69 int len=strlen(buf); 70 int now=root; 71 int res=0; 72 for ( int i=0;i<len;i++ ) 73 //沿着整个文本串移动,每移动到一个字符(节点) 时,通过失配指针不断找寻模式串 ,重点为源头,找到一个就将其标记清除 74 { 75 now=nxt[now][buf[i]-'a']; 76 int tmp=now; 77 while ( tmp!=root ) 78 { 79 res+=end[tmp]; 80 end[tmp]=0; 81 tmp=fail[tmp]; 82 } 83 } 84 return res; //返回单词个数 85 } 86 void debug() 87 { 88 for ( int i=0;i<L;i++ ) 89 { 90 printf("%id=%3d,fail=%3d,end=%3d,chi=[",i,fail[i],end[i]); 91 for ( int j=0;j<26;j++ ) printf("%2d",nxt[i][j]); 92 printf("] "); 93 } 94 } 95 }; 96 char buf[maxn*2]; 97 Trie ac; 98 int main() 99 { 100 int T,n; 101 scanf("%d",&T); 102 while ( T-- ) 103 { 104 scanf("%d",&n); 105 ac.init(); 106 for ( int i=0;i<n;i++ ) 107 { 108 scanf("%s",buf); 109 ac.insert(buf); 110 } 111 ac.build(); 112 scanf("%s",buf); 113 printf("%d ",ac.query(buf)); 114 } 115 return 0; 116 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<queue> 6 using namespace std; 7 const int maxn=500010; 8 const int maxm=26; 9 10 struct Trie 11 { 12 int nxt[maxn][maxm],fail[maxn],end[maxn]; 13 int root,L; 14 int newnode() 15 { 16 for ( int i=0;i<maxm;i++ ) nxt[L][i]=-1; 17 end[L++]=0; 18 return L-1; 19 } 20 void init() 21 { 22 L=0; 23 root=newnode(); 24 memset(end,0,sizeof(end)); 25 } 26 void insert(char buf[]) 27 { 28 int len=strlen(buf); 29 int now=root; 30 for ( int i=0;i<len;i++ ) 31 { 32 int x=buf[i]-'a'; 33 if ( nxt[now][x]==-1 ) nxt[now][x]=newnode(); 34 now=nxt[now][x]; 35 } 36 end[now]++; 37 } 38 void build() 39 { 40 queue<int>que; 41 fail[root]=root; 42 for (int i=0;i<maxm;i++ ) 43 { 44 if ( nxt[root][i]==-1 ) nxt[root][i]=root; 45 else 46 { 47 int x=nxt[root][i]; 48 fail[x]=root; 49 que.push(x); 50 } 51 } 52 while ( !que.empty() ) 53 { 54 int now=que.front(); 55 que.pop(); 56 for ( int i=0;i<maxm;i++ ) 57 { 58 if ( nxt[now][i]==-1 ) 59 nxt[now][i]=nxt[fail[now]][i]; 60 else 61 { 62 int x=nxt[now][i]; 63 fail[x]=nxt[fail[now]][i]; 64 que.push(x); 65 } 66 } 67 } 68 } 69 int query(char buf[]) 70 { 71 int len=strlen(buf); 72 int now=root; 73 int res=0; 74 for ( int i=0;i<len;i++ ) 75 { 76 int x=buf[i]-'a'; 77 now=nxt[now][x]; 78 int tmp=now; 79 while ( tmp!=root ) 80 { 81 res+=end[tmp]; 82 end[tmp]=0; 83 tmp=fail[tmp]; 84 } 85 } 86 return res; 87 } 88 void debug() 89 { 90 for ( int i=0;i<L;i++ ) 91 { 92 printf("%id=%3d,fail=%3d,end=%3d,chi=[",i,fail[i],end[i]); 93 for ( int j=0;j<26;j++ ) printf("%2d",nxt[i][j]); 94 printf("] "); 95 } 96 } 97 }; 98 char buf[maxn*2]; 99 Trie ac; 100 int main() 101 { 102 int T,n; 103 scanf("%d",&T); 104 while ( T-- ) 105 { 106 scanf("%d",&n); 107 ac.init(); 108 for ( int i=0;i<n;i++ ) 109 { 110 scanf("%s",buf); 111 ac.insert(buf); 112 } 113 ac.build(); 114 scanf("%s",buf); 115 printf("%d ",ac.query(buf)); 116 } 117 return 0; 118 }
2.(HDOJ2896)http://acm.hdu.edu.cn/showproblem.php?pid=2896
分析:只需要将原模板中的end[i]标记为是第几个模式串的id(本来记录的是数量),再另外设置一个bool型的vis数组,当tmp指针访问到某一单词的结尾时,将编号为该单词的id的数在vis数组中标为true。同时注意是所有的ascii而不是只有小写英文字母,nxt数组的第二维开128的大小
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 using namespace std; 6 const int maxn=500010; 7 const int maxm=128; 8 const int maxk=510; 9 char buf[maxn*2]; 10 bool vis[maxk]; 11 12 struct Trie 13 { 14 int nxt[maxn][maxm],end[maxn],fail[maxn]; 15 int root,L; 16 int newnode() 17 { 18 for ( int i=0;i<maxm;i++ ) nxt[L][i]=-1; 19 end[L++]=0; 20 return L-1; 21 } 22 void init() 23 { 24 L=0; 25 root=newnode(); 26 memset(end,0,sizeof(end)); 27 } 28 void insert(char buf[],int id) 29 { 30 int len=strlen(buf); 31 int now=root; 32 for ( int i=0;i<len;i++ ) 33 { 34 int x=buf[i]; 35 if ( nxt[now][x]==-1 ) nxt[now][x]=newnode(); 36 now=nxt[now][x]; 37 } 38 end[now]=id; 39 } 40 void build() 41 { 42 queue<int>que; 43 fail[root]=root; 44 for ( int i=0;i<maxm;i++ ) 45 { 46 if ( nxt[root][i]==-1 ) nxt[root][i]=root; 47 else 48 { 49 int x=nxt[root][i]; 50 fail[x]=root; 51 que.push(x); 52 } 53 } 54 while ( !que.empty() ) 55 { 56 int now=que.front(); 57 que.pop(); 58 for ( int i=0;i<maxm;i++ ) 59 { 60 if ( nxt[now][i]==-1 ) nxt[now][i]=nxt[fail[now]][i]; 61 else 62 { 63 int x=nxt[now][i]; 64 fail[x]=nxt[fail[now]][i]; 65 que.push(x); 66 } 67 } 68 } 69 } 70 bool query(char buf[]) 71 { 72 int len=strlen(buf); 73 int now=root; 74 bool flag=false; 75 for ( int i=0;i<len;i++ ) 76 { 77 int x=buf[i]; 78 now=nxt[now][x]; 79 int tmp=now; 80 while ( tmp!=root ) 81 { 82 if ( end[tmp]!=0 ) { 83 vis[end[tmp]]=true; 84 flag=true; 85 } 86 tmp=fail[tmp]; 87 } 88 } 89 return flag; 90 } 91 }; 92 Trie ac; 93 int main() 94 { 95 int T,n,m,ans; 96 while ( scanf("%d",&n)!=EOF ) 97 { 98 ans=0; 99 ac.init(); 100 for ( int i=1;i<=n;i++ ) 101 { 102 scanf("%s",buf); 103 ac.insert(buf,i); 104 } 105 ac.build(); 106 scanf("%d",&m); 107 for ( int i=1;i<=m;i++ ) 108 { 109 memset(vis,false,sizeof(vis)); 110 scanf("%s",buf); 111 if ( ac.query(buf) ) { 112 printf("web %d:",i); 113 for ( int j=1;j<=n;j++ ) 114 { 115 if ( vis[j] ) printf(" %d",j); 116 } 117 printf(" "); 118 ans++; 119 } 120 } 121 printf("total: %d ",ans); 122 } 123 return 0; 124 }
3.(HDOJ3065)http://acm.hdu.edu.cn/showproblem.php?pid=3065
分析:只需要将上一题的bool型改成int型数组输出即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 using namespace std; 6 const int maxn=500010; 7 const int maxq=2000010; 8 const int maxm=128; 9 const int maxk=1010; 10 char buf[maxq]; 11 int vis[maxk]; 12 char s[maxk][55]; 13 14 struct Trie 15 { 16 int nxt[maxn][maxm],end[maxn],fail[maxn]; 17 int root,L; 18 int newnode() 19 { 20 for ( int i=0;i<maxm;i++ ) nxt[L][i]=-1; 21 end[L++]=0; 22 return L-1; 23 } 24 void init() 25 { 26 L=0; 27 root=newnode(); 28 memset(end,0,sizeof(end)); 29 } 30 void insert(char buf[],int id) 31 { 32 int len=strlen(buf); 33 int now=root; 34 for ( int i=0;i<len;i++ ) 35 { 36 int x=buf[i]; 37 if ( nxt[now][x]==-1 ) nxt[now][x]=newnode(); 38 now=nxt[now][x]; 39 } 40 end[now]=id; 41 } 42 void build() 43 { 44 queue<int>que; 45 fail[root]=root; 46 for ( int i=0;i<maxm;i++ ) 47 { 48 if ( nxt[root][i]==-1 ) nxt[root][i]=root; 49 else 50 { 51 int x=nxt[root][i]; 52 fail[x]=root; 53 que.push(x); 54 } 55 } 56 while ( !que.empty() ) 57 { 58 int now=que.front(); 59 que.pop(); 60 for ( int i=0;i<maxm;i++ ) 61 { 62 if ( nxt[now][i]==-1 ) nxt[now][i]=nxt[fail[now]][i]; 63 else 64 { 65 int x=nxt[now][i]; 66 fail[x]=nxt[fail[now]][i]; 67 que.push(x); 68 } 69 } 70 } 71 } 72 void query(char buf[]) 73 { 74 int len=strlen(buf); 75 int now=root; 76 for ( int i=0;i<len;i++ ) 77 { 78 int x=buf[i]; 79 now=nxt[now][x]; 80 int tmp=now; 81 while ( tmp!=root ) 82 { 83 if ( end[tmp]!=0 ) vis[end[tmp]]++; 84 tmp=fail[tmp]; 85 } 86 } 87 return; 88 } 89 }; 90 Trie ac; 91 int main() 92 { 93 int T,n,m; 94 while ( scanf("%d",&n)!=EOF ) 95 { 96 ac.init(); 97 for ( int i=1;i<=n;i++ ) 98 { 99 scanf("%s",s[i]); 100 ac.insert(s[i],i); 101 } 102 ac.build(); 103 scanf("%s",buf); 104 memset(vis,0,sizeof(vis)); 105 ac.query(buf); 106 for ( int i=1;i<=n;i++ ) 107 { 108 if ( vis[i]!=0 ) printf("%s: %d ",s[i],vis[i]); 109 } 110 } 111 return 0; 112 }
4.(POJ2778)http://poj.org/problem?id=2778
题意:给出n个患病的DNA序列,问序列长度为m的,且不包含患病的DNA序列有多少种。
分析:AC自动机+乘法矩阵,https://blog.csdn.net/morgan_xww/article/details/7834801推荐此博客有较详细的解释
大致过程:将end[maxn]数组改成bool型,若为true表示该点是危险点,为false为安全点(初始时全为false)( 显然根结点是安全结点。 一个非根结点是危险结点的充要条件是: 它的路径字符串本身就是一个不良单词 ,或 它的路径字符串的后缀对应的结点(即fail[i])是危险结点)。增加longlong型的mat[maxn][maxn]数组,mat[i][j]表示节点i走一步到节点j有多少方案,当i和j都为安全点时才被计算进mat数组中(即所有满足条件的答案/不含模式串)。增加res[maxn][maxn]为mat阶乘m次后的答案。最后的答案为res[0][i](i从[0,L))的求和
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<queue> 6 using namespace std; 7 typedef long long ll; 8 const int maxn=1e3+10; 9 const int maxm=4; 10 const int mod=1e5; 11 12 struct Trie 13 { 14 int nxt[maxn][maxm],fail[maxn]; 15 ll mat[maxn][maxn]; 16 bool end[maxn]; 17 int root,L; 18 int newnode() 19 { 20 for ( int i=0;i<maxm;i++ ) nxt[L][i]=-1; 21 end[L++]=0; 22 return L-1; 23 } 24 void init() 25 { 26 L=0; 27 root=newnode(); 28 memset(end,false,sizeof(end)); 29 memset(mat,0,sizeof(mat)); 30 } 31 int getid(char c) 32 { 33 if ( c=='A' ) return 0; 34 else if ( c=='C' ) return 1; 35 else if ( c=='T' ) return 2; 36 else if ( c=='G' ) return 3; 37 } 38 void insert(char buf[]) 39 { 40 int len=strlen(buf); 41 int now=root; 42 for ( int i=0;i<len;i++ ) 43 { 44 int x=getid(buf[i]); 45 if ( nxt[now][x]==-1 ) nxt[now][x]=newnode(); 46 now=nxt[now][x]; 47 } 48 end[now]=true; 49 } 50 void build() 51 { 52 queue<int>que; 53 fail[root]=root; 54 for (int i=0;i<maxm;i++ ) 55 { 56 if ( nxt[root][i]==-1 ) nxt[root][i]=root; 57 else 58 { 59 int x=nxt[root][i]; 60 fail[x]=root; 61 que.push(x); 62 } 63 } 64 while ( !que.empty() ) 65 { 66 int now=que.front(); 67 que.pop(); 68 if ( end[fail[now]] ) end[now]=true; 69 for ( int i=0;i<maxm;i++ ) 70 { 71 if ( nxt[now][i]==-1 ) 72 nxt[now][i]=nxt[fail[now]][i]; 73 else 74 { 75 int x=nxt[now][i]; 76 fail[x]=nxt[fail[now]][i]; 77 que.push(x); 78 } 79 } 80 } 81 } 82 void setmat() 83 { 84 for ( int i=0;i<L;i++ ) 85 { 86 for ( int j=0;j<maxm;j++ ) 87 { 88 if ( !end[i] && !end[nxt[i][j]] ) mat[i][nxt[i][j]]++; 89 } 90 } 91 } 92 void debug() 93 { 94 for ( int i=0;i<L;i++ ) 95 { 96 for ( int j=0;j<L;j++ ) printf("%d ",mat[i][j]); 97 printf(" "); 98 } 99 } 100 ll res[maxn][maxn],tmp[maxn][maxn]; 101 void mul(ll a[][maxn],ll b[][maxn]) 102 { 103 for ( int i=0;i<L;i++ ) 104 { 105 for ( int j=0;j<L;j++ ) 106 { 107 tmp[i][j]=0; 108 for ( int k=0;k<L;k++ ) tmp[i][j]=(tmp[i][j]+a[i][k]*b[k][j])%mod; 109 } 110 } 111 for ( int i=0;i<L;i++ ) 112 { 113 for ( int j=0;j<L;j++ ) a[i][j]=tmp[i][j]%mod; 114 } 115 } 116 void pow(ll k) 117 { 118 memset(res,0,sizeof(res)); 119 for ( int i=0;i<L;i++ ) res[i][i]=1; 120 while ( k ) 121 { 122 if ( k&1 ) mul(res,mat); 123 mul(mat,mat); 124 k/=2; 125 } 126 } 127 }; 128 char buf[105]; 129 Trie ac; 130 int main() 131 { 132 int T,n,m; 133 ll ans; 134 while ( scanf("%d%d",&n,&m)!=EOF ) 135 { 136 ac.init(); 137 for ( int i=0;i<n;i++ ) 138 { 139 scanf("%s",buf); 140 ac.insert(buf); 141 } 142 ac.build(); 143 ac.setmat(); 144 ac.pow(m); 145 ans=0; 146 for ( int i=0;i<ac.L;i++ ) 147 { 148 ans=(ans+ac.res[0][i])%mod; 149 } 150 printf("%lld ",ans); 151 } 152 return 0; 153 }
5.(HDOJ2243)http://acm.hdu.edu.cn/showproblem.php?pid=2243
分析:和上题类似,不过不同之处有两点:1.该题是至少包含一个模式串(上一题是一个都没有) 2.该题最后得到的长度<=n(上一题是刚好为n)
分析:大致做法于上题相同,难点在于如何解决两个不同。对于第一个不同采用反面的想法(即算出所有答案减去不含模式串的答案就为当前的答案),对于第二个考虑通过矩阵构造的方法
记初始的mat矩阵为A,若想求得A^1+A^2……+A^n则需要构造一个这样的矩阵。
注意:将运算过程中的变量和矩阵的元素定义为unsigned long long,就能实现自动对2^64自动取mod(即不用特地去取模)。
特别注意两个矩阵的构造方法(一个是求26^1到26^n的求和,另外一个是A^1到A^n的求和),关注几次幂和矩阵大小
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<queue> 6 #include<cmath> 7 using namespace std; 8 typedef long long ll; 9 typedef unsigned long long ull; 10 const int maxn=1e3+10; 11 const int maxm=26; 12 //const ll mod=0x3f3f3f3f; 13 14 struct Trie 15 { 16 int nxt[maxn][maxm],fail[maxn]; 17 ull mat[maxn][maxn],num[4][4]; 18 bool end[maxn]; 19 int root,L; 20 int newnode() 21 { 22 for ( int i=0;i<maxm;i++ ) nxt[L][i]=-1; 23 end[L++]=0; 24 return L-1; 25 } 26 void init() 27 { 28 L=0; 29 root=newnode(); 30 memset(end,false,sizeof(end)); 31 memset(mat,0,sizeof(mat)); 32 } 33 int getid(char c) 34 { 35 return c-'a'; 36 } 37 void insert(char buf[]) 38 { 39 int len=strlen(buf); 40 int now=root; 41 for ( int i=0;i<len;i++ ) 42 { 43 int x=getid(buf[i]); 44 if ( nxt[now][x]==-1 ) nxt[now][x]=newnode(); 45 now=nxt[now][x]; 46 } 47 end[now]=true; 48 } 49 void build() 50 { 51 queue<int>que; 52 fail[root]=root; 53 for (int i=0;i<maxm;i++ ) 54 { 55 if ( nxt[root][i]==-1 ) nxt[root][i]=root; 56 else 57 { 58 int x=nxt[root][i]; 59 fail[x]=root; 60 que.push(x); 61 } 62 } 63 while ( !que.empty() ) 64 { 65 int now=que.front(); 66 que.pop(); 67 if ( end[fail[now]] ) end[now]=true; 68 for ( int i=0;i<maxm;i++ ) 69 { 70 if ( nxt[now][i]==-1 ) 71 nxt[now][i]=nxt[fail[now]][i]; 72 else 73 { 74 int x=nxt[now][i]; 75 fail[x]=nxt[fail[now]][i]; 76 que.push(x); 77 } 78 } 79 } 80 } 81 void setmat() 82 { 83 for ( int i=0;i<L;i++ ) 84 { 85 for ( int j=0;j<maxm;j++ ) 86 { 87 if ( !end[i] && !end[nxt[i][j]] ) mat[i][nxt[i][j]]++; 88 } 89 } 90 for ( int i=0;i<=L;i++ ) mat[i][L]=1; 91 } 92 void debug() 93 { 94 for ( int i=0;i<L;i++ ) 95 { 96 for ( int j=0;j<L;j++ ) printf("%d ",mat[i][j]); 97 printf(" "); 98 } 99 } 100 ull res[maxn][maxn],tmp[maxn][maxn]; 101 void mul(ull a[][maxn],ull b[][maxn],ll l) 102 { 103 for ( int i=0;i<l;i++ ) 104 { 105 for ( int j=0;j<l;j++ ) 106 { 107 tmp[i][j]=0; 108 for ( int k=0;k<l;k++ ) tmp[i][j]=(tmp[i][j]+a[i][k]*b[k][j]); 109 } 110 } 111 for ( int i=0;i<l;i++ ) 112 { 113 for ( int j=0;j<l;j++ ) a[i][j]=tmp[i][j]; 114 } 115 } 116 void pow(ll k,ll l) 117 { 118 memset(res,0,sizeof(res)); 119 for ( int i=0;i<l;i++ ) res[i][i]=1; 120 while ( k ) 121 { 122 if ( k&1 ) mul(res,mat,l); 123 mul(mat,mat,l); 124 k/=2; 125 } 126 } 127 }; 128 char buf[10]; 129 Trie ac; 130 int main() 131 { 132 int T,n,m; 133 ull ans,sum; 134 while ( scanf("%d%d",&n,&m)!=EOF ) 135 { 136 ac.init(); 137 for ( int i=0;i<n;i++ ) 138 { 139 scanf("%s",buf); 140 ac.insert(buf); 141 } 142 ac.build(); 143 ac.setmat(); 144 //ac.debug(); 145 ac.pow(m,ac.L+1); 146 ans=0; 147 for ( int i=0;i<=ac.L;i++ ) 148 { 149 ans=(ans+ac.res[0][i]); 150 } 151 ac.mat[0][0]=26; 152 ac.mat[0][1]=ac.mat[1][1]=1; 153 ac.mat[1][0]=0; 154 ac.pow(m+1,2); 155 sum=ac.res[0][1]; 156 ans=(sum-ans); 157 printf("%I64u ",ans); 158 } 159 return 0; 160 }
6.(HDOJ2457)http://acm.hdu.edu.cn/showproblem.php?pid=2457
题意:给定N(N <= 50)个长度不超过20的模式串,再给定一个长度为M(M <= 1000)的目标串S,求在目标串S上最少改变多少字符,可以使得它不包含任何的模式串(所有串只有ACGT四种字符)
分析:AC自动机+dp。先通过AC自动机得到Trie图,然后通过Trie图去构建字符串。设dp[i][j]表示长度为i,状态为j(这里的状态和节点的编号相对应)的字符串变成目标串前i位需要的最少操作次数。初始化:dp[0][0]=0(初始状态),其他一律设置为inf。具体转移及细节见代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<queue> 6 using namespace std; 7 const int maxn=1010; 8 const int maxm=4; 9 const int inf=1e9; 10 int dp[maxn][maxn]; 11 12 struct Trie 13 { 14 int nxt[maxn][maxm],fail[maxn]; 15 bool end[maxn]; 16 int root,L; 17 int newnode() 18 { 19 for ( int i=0;i<maxm;i++ ) nxt[L][i]=-1; 20 end[L++]=0; 21 return L-1; 22 } 23 void init() 24 { 25 L=0; 26 root=newnode(); 27 memset(end,false,sizeof(end)); 28 } 29 int getid(char c) 30 { 31 if ( c=='A' ) return 0; 32 else if ( c=='T' ) return 1; 33 else if ( c=='G' ) return 2; 34 else if ( c=='C' ) return 3; 35 } 36 void insert(char buf[]) 37 { 38 int len=strlen(buf); 39 int now=root; 40 for ( int i=0;i<len;i++ ) 41 { 42 int x=getid(buf[i]); 43 if ( nxt[now][x]==-1 ) nxt[now][x]=newnode(); 44 now=nxt[now][x]; 45 } 46 end[now]=true; 47 } 48 void build() 49 { 50 queue<int>que; 51 fail[root]=root; 52 for (int i=0;i<maxm;i++ ) 53 { 54 if ( nxt[root][i]==-1 ) nxt[root][i]=root; 55 else 56 { 57 int x=nxt[root][i]; 58 fail[x]=root; 59 que.push(x); 60 } 61 } 62 while ( !que.empty() ) 63 { 64 int now=que.front(); 65 que.pop(); 66 if ( end[fail[now]] ) end[now]=true; 67 for ( int i=0;i<maxm;i++ ) 68 { 69 if ( nxt[now][i]==-1 ) 70 nxt[now][i]=nxt[fail[now]][i]; 71 else 72 { 73 int x=nxt[now][i]; 74 fail[x]=nxt[fail[now]][i]; 75 que.push(x); 76 } 77 } 78 } 79 } 80 int query(char buf[]) 81 { 82 int len=strlen(buf); 83 int now=root; 84 int res=0; 85 for ( int i=0;i<len;i++ ) 86 { 87 int x=getid(buf[i]); 88 now=nxt[now][x]; 89 int tmp=now; 90 while ( tmp!=root ) 91 { 92 res+=end[tmp]; 93 end[tmp]=0; 94 tmp=fail[tmp]; 95 } 96 } 97 return res; 98 } 99 }; 100 char buf[maxn]; 101 Trie ac; 102 int main() 103 { 104 int n,h=0; 105 while ( scanf("%d",&n)!=EOF && n ) 106 { 107 ac.init(); 108 for ( int i=0;i<n;i++ ) 109 { 110 scanf("%s",buf); 111 ac.insert(buf); 112 } 113 ac.build(); 114 scanf("%s",buf); 115 int len=strlen(buf); 116 for ( int i=0;i<=len;i++ ) 117 for ( int j=0;j<ac.L;j++ ) dp[i][j]=inf; 118 dp[0][ac.root]=0; 119 for ( int i=0;i<len;i++ ) 120 for ( int j=0;j<ac.L;j++ ) 121 if ( dp[i][j]<inf ) 122 { 123 for ( int k=0;k<maxm;k++ ) 124 { 125 int news=ac.nxt[j][k]; 126 if ( ac.end[news] ) continue; 127 int x=ac.getid(buf[i]); 128 int add; 129 if ( x==k ) add=0; 130 else add=1; 131 dp[i+1][news]=min(dp[i+1][news],add+dp[i][j]); 132 } 133 } 134 int ans=inf; 135 for ( int j=0;j<ac.L;j++ ) ans=min(ans,dp[len][j]); 136 printf("Case %d: ",++h); 137 if ( ans==inf ) printf("-1 "); 138 else printf("%d ",ans); 139 } 140 return 0; 141 }
待做:1.(POJ1699)http://poj.org/problem?id=1699
2.(HDOJ2296)http://acm.hdu.edu.cn/showproblem.php?pid=2296
3.(HDOJ3341)http://acm.hdu.edu.cn/showproblem.php?pid=3341
4.(HDOJ3247)http://acm.hdu.edu.cn/showproblem.php?pid=3247
小结:AC自动机一般适用于多串匹配,往往是通过构造去求一些关于不含模式串的字符串类型的题目,构造过程往往在Trie图中按照一定要求(一般为是否该点/状态为安全点)进行,Trie图中的每一个点代表一个状态,从一个点有maxm(字符类型的总个数)个移动方向,危险点往往不参与构造(即只有安全点才参与)。