AC自动机在trie树上实现KMP的一种数据结构,可以完成多模式串的匹配,核心要理解fail指针的含义,即让当前字符失配时跳转到具有最长公共前后缀的字符继续匹配,从根节点到当前节点(s)fail指针的节点(p)的路径字符串必定为从根节点到节点s的路径字符串的一个后缀,还有理解trie图,当字符串在trie树上行走没有路可走时,fail指针指向的节点可相当于字符串要走的下一点,然后再无限匹配下去,具体介绍看trie图的构建、活用与理解。
学习资料:http://blog.csdn.net/niushuai666/article/details/7002823
http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d
模板题
1.HDU 2222
题意:统计目标串中模式串的个数。
#include <bits/stdc++.h> using namespace std; const int N = 500010; struct Trie { int ch[N][26],fail[N],last[N]; int root,sz; int newnode() { for(int i=0;i<26;i++)ch[sz][i]=-1; last[sz++]=0; return sz-1; } void init() { sz=0; root=newnode(); } void insert(char s[]) { int len=strlen(s); int now=root; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(ch[now][id]==-1) ch[now][id]=newnode(); now=ch[now][id]; } last[now]++; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<26;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); for(int i=0;i<26;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } int query(char s[]) { int len=strlen(s); int now=root,res=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; now=ch[now][id]; int temp=now; while(temp!=root) { res+=last[temp]; last[temp]=0; temp=fail[temp]; } } return res; } }ac; char str[1000010]; int main() { int T,n; scanf("%d",&T); while(T--) { scanf("%d",&n); ac.init(); for(int i=0;i<n;i++) { scanf("%s",str); ac.insert(str); } ac.build(); scanf("%s",str); printf("%d\n",ac.query(str)); } }
2.HDU 2896
题意:统计每个目标串中模式串的个数和id。
#include <bits/stdc++.h> using namespace std; const int N = 100010; struct Trie { int ch[N][128],fail[N],last[N]; int root,sz; int newnode() { for(int i=0;i<128;i++)ch[sz][i]=-1; last[sz++]=0; return sz-1; } void init() { sz=0; root=newnode(); } void insert(char s[],int num) { int len=strlen(s); int now=root; for(int i=0;i<len;i++) { int id=s[i]; if(ch[now][id]==-1) ch[now][id]=newnode(); now=ch[now][id]; } last[now]=num; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<128;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); for(int i=0;i<128;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } bool vis[510]; bool query(char s[],int n,int num) { int len=strlen(s); int now=root; bool flag=false; memset(vis,false,sizeof(vis)); for(int i=0;i<len;i++) { int id=s[i]; now=ch[now][id]; int temp=now; while(temp!=root) { if(last[temp]) { vis[last[temp]]=true; flag=true; } temp=fail[temp]; } } if(!flag)return false; printf("web %d:",num); for(int i=1;i<=n;i++) if(vis[i])printf(" %d",i); puts(""); return true; } }ac; char str[10010]; int main() { int n,m; while(scanf("%d",&n)>0) { ac.init(); for(int i=1;i<=n;i++) { scanf("%s",str); ac.insert(str,i); } ac.build(); int ans=0; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",str); if(ac.query(str,n,i))ans++; } printf("total: %d\n",ans); } }
3.HDU 3065
题意:统计每个模式串在目标串中出现的次数。
#include <bits/stdc++.h> using namespace std; const int N = 50010; char str[1010][110]; struct Trie { int ch[N][128],fail[N],last[N]; int root,sz; int newnode() { for(int i=0;i<128;i++)ch[sz][i]=-1; last[sz++]=0; return sz-1; } void init() { sz=0; root=newnode(); } void insert(char s[],int num) { int len=strlen(s); int now=root; for(int i=0;i<len;i++) { int id=s[i]; if(ch[now][id]==-1) ch[now][id]=newnode(); now=ch[now][id]; } last[now]=num; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<128;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); for(int i=0;i<128;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } int vis[1010]; void query(char s[],int n) { int len=strlen(s); int now=root; memset(vis,0,sizeof(vis)); for(int i=0;i<len;i++) { int id=s[i]; now=ch[now][id]; int temp=now; while(temp!=root) { if(last[temp]) { vis[last[temp]]++; } temp=fail[temp]; } } for(int i=1;i<=n;i++) if(vis[i])printf("%s: %d\n",str[i],vis[i]); } }ac; char s[2000010]; int main() { int n,m; while(scanf("%d",&n)>0) { ac.init(); for(int i=1;i<=n;i++) { scanf("%s",str[i]); ac.insert(str[i],i); } ac.build(); scanf("%s",s); ac.query(s,n); } }
4.ZOJ 3228
题意:统计模式串在目标串中出现的次数,一种是可重叠的,一种是不可重叠的。
#include <bits/stdc++.h> using namespace std; const int N = 600010; struct Trie { int ch[N][26],fail[N],len[N]; int root,sz; int newnode() { for(int i=0;i<26;i++)ch[sz][i]=-1; last[sz++]=0; return sz-1; } void init() { sz=0; root=newnode(); } int insert(char s[]) { int lens=strlen(s); int now=root; for(int i=0;i<lens;i++) { int id=s[i]-'a'; if(ch[now][id]==-1) { ch[now][id]=newnode(); len[ch[now][id]]=i+1; } now=ch[now][id]; } return now; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<26;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); for(int i=0;i<26;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } int cnt[N][2],last[N]; void query(char s[]) { int lens=strlen(s); int now=root; memset(cnt,0,sizeof(cnt)); memset(last,0,sizeof(last)); for(int i=0;i<lens;i++) { int id=s[i]-'a'; now=ch[now][id]; int temp=now; while(temp!=root) { cnt[temp][0]++; if(i+1-last[temp]>=len[temp]) { last[temp]=i+1; cnt[temp][1]++; } temp=fail[temp]; } } } }ac; char s[N],str[20]; int pos[N],type[N]; int main() { int n,cas=1; while(scanf("%s",s)>0) { ac.init(); scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d%s",&type[i],str); pos[i]=ac.insert(str); } ac.build(); ac.query(s); printf("Case %d\n",cas++); for(int i=0;i<n;i++)printf("%d\n",ac.cnt[pos[i]][type[i]]); puts(""); } }
矩阵
1.POJ 2778
题意:有m种DNA序列是有病毒的,问有多少种长度为n的DNA序列不包含任何一种有病毒的DNA序列。
分析:在trie图上走n步不包含病毒的节点,建立好矩阵进行状态转移,和有向图中走n步从一点到另一点一样,直接矩阵快速幂。
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespace std; const int N = 110; const int mod = 100000; struct matrix { int m[N][N],n; matrix(){} matrix(int _n) { n=_n; for(int i=0;i<n;i++) for(int j=0;j<n;j++) m[i][j]=0; } matrix operator*(const matrix &a)const { matrix res=matrix(n); for(int k=0;k<n;k++) for(int i=0;i<n;i++) for(int j=0;j<n;j++) { res.m[i][j]=(res.m[i][j]+1ll*m[i][k]*a.m[k][j])%mod; } return res; } }; struct Trie { int ch[N][4],fail[N]; bool last[N]; int root,sz; int newnode() { for(int i=0;i<4;i++)ch[sz][i]=-1; last[sz++]=false; return sz-1; } void init() { sz=0; root=newnode(); } int getid(char ch) { if(ch=='A')return 0; else if(ch=='C')return 1; else if(ch=='G')return 2; else return 3; } void insert(char s[]) { int lens=strlen(s); int now=root; for(int i=0;i<lens;i++) { int id=getid(s[i]); if(ch[now][id]==-1) { ch[now][id]=newnode(); } now=ch[now][id]; } last[now]=true; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<4;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); if(last[fail[now]])last[now]=true; for(int i=0;i<4;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } matrix getmat() { matrix res=matrix(sz); for(int i=0;i<sz;i++) for(int j=0;j<4;j++) if(!last[ch[i][j]])res.m[i][ch[i][j]]++; return res; } }ac; matrix quick_mod(matrix a,int n) { matrix res=matrix(ac.sz); for(int i=0;i<ac.sz;i++)res.m[i][i]=1; while(n) { if(n&1)res=res*a; a=a*a; n>>=1; } return res; } int main() { int n,m; while(scanf("%d%d",&n,&m)>0) { ac.init(); for(int i=1;i<=n;i++) { char s[15]; scanf("%s",s); ac.insert(s); } ac.build(); matrix a=ac.getmat(); a=quick_mod(a,m); int ans=0; for(int i=0;i<ac.sz;i++) { ans+=a.m[0][i]; } ans%=mod; printf("%d\n",ans); } }
2.HDU 2243
题意:给你n 个单词,求出满足以下条件的单词个数:长度不大于L 且单词中至少包含一个子串为前面n 个单词中任意一个。
分析:先求出所有单词的数量 26^1+ 26^2+ ... + 26^L,可以构造矩阵乘法求出。然后求出所有不包含 n 个单词的串的数量,两者相减就是答案了。
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespace std; typedef unsigned long long ull; const int N = 50; const int mod = 100000; struct matrix { ull m[N*2][N*2],n; matrix(){} matrix(int _n) { n=_n; for(int i=0;i<n;i++) for(int j=0;j<n;j++) m[i][j]=0; } matrix operator*(const matrix &a)const { matrix res=matrix(n); for(int k=0;k<n;k++) for(int i=0;i<n;i++) for(int j=0;j<n;j++) { res.m[i][j]=res.m[i][j]+m[i][k]*a.m[k][j]; } return res; } }; struct Trie { int ch[N][26],fail[N]; bool last[N]; int root,sz; int newnode() { for(int i=0;i<26;i++)ch[sz][i]=-1; last[sz++]=false; return sz-1; } void init() { sz=0; root=newnode(); } void insert(char s[]) { int lens=strlen(s); int now=root; for(int i=0;i<lens;i++) { int id=s[i]-'a'; if(ch[now][id]==-1) { ch[now][id]=newnode(); } now=ch[now][id]; } last[now]=true; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<26;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); if(last[fail[now]])last[now]=true; for(int i=0;i<26;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } matrix getmat() { matrix res=matrix(sz*2); for(int i=0;i<sz;i++) for(int j=0;j<26;j++) if(!last[ch[i][j]])res.m[i][ch[i][j]]++; for(int i=0;i<sz;i++)res.m[i][i+sz]=res.m[i+sz][i+sz]=1; return res; } }ac; matrix quick_mod(matrix a,int n) { matrix res=matrix(a.n); for(int i=0;i<a.n;i++)res.m[i][i]=1; while(n) { if(n&1)res=res*a; a=a*a; n>>=1; } return res; } int main() { int n,m; char s[15]; while(scanf("%d%d",&n,&m)>0) { ac.init(); for(int i=1;i<=n;i++) { scanf("%s",s); ac.insert(s); } ac.build(); matrix a=ac.getmat(); a=quick_mod(a,m); ull ans1=0; for(int i=0;i<ac.sz*2;i++) { ans1+=a.m[0][i]; } ans1--; matrix b=matrix(2); b.m[0][0]=26;b.m[0][1]=1; b.m[1][0]=0;b.m[1][1]=1; b=quick_mod(b,m); ull ans2=0; ans2=b.m[0][0]+b.m[0][1]; ans2--; printf("%I64u\n",ans2-ans1); } }
DP
1.POJ 1625
题意:给出病毒串,求长度为n且不含病毒串的DNA种数。
分析:dp[i][j]表示长度为i时,以自动机上结点编号为j结尾的种数。dp[i][j]+=dp[i-1][k],k是自动机上能转移到j的结点,且j、k都不是病毒串的结尾。(dp[0][0]=1)
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <map> using namespace std; const int N = 110; const int mod = 100000; map<char,int>mp; struct BigInt { const static int mod = 10000; const static int DLEN = 4; int a[600],len; BigInt() { memset(a,0,sizeof(a)); len = 1; } BigInt(int v) { memset(a,0,sizeof(a)); len = 0; do { a[len++] = v%mod; v /= mod; }while(v); } BigInt(const char s[]) { memset(a,0,sizeof(a)); int L = strlen(s); len = L/DLEN; if(L%DLEN)len++; int index = 0; for(int i = L-1;i >= 0;i -= DLEN) { int t = 0; int k = i - DLEN + 1; if(k < 0)k = 0; for(int j = k;j <= i;j++) t = t*10 + s[j] - '0'; a[index++] = t; } } BigInt operator +(const BigInt &b)const { BigInt res; res.len = max(len,b.len); for(int i = 0;i <= res.len;i++) res.a[i] = 0; for(int i = 0;i < res.len;i++) { res.a[i] += ((i < len)?a[i]:0)+((i < b.len)?b.a[i]:0); res.a[i+1] += res.a[i]/mod; res.a[i] %= mod; } if(res.a[res.len] > 0)res.len++; return res; } BigInt operator *(const BigInt &b)const { BigInt res; for(int i = 0; i < len;i++) { int up = 0; for(int j = 0;j < b.len;j++) { int temp = a[i]*b.a[j] + res.a[i+j] + up; res.a[i+j] = temp%mod; up = temp/mod; } if(up != 0) res.a[i + b.len] = up; } res.len = len + b.len; while(res.a[res.len - 1] == 0 &&res.len > 1)res.len--; return res; } void output() { printf("%d",a[len-1]); for(int i = len-2;i >=0 ;i--) printf("%04d",a[i]); printf("\n"); } }; struct matrix { int m[N][N]; int n; matrix(){} matrix(int _n) { n=_n; for(int i=0;i<n;i++) for(int j=0;j<n;j++) m[i][j]=0; } }; struct Trie { int ch[N][256],fail[N]; bool last[N]; int root,sz; int newnode() { for(int i=0;i<256;i++)ch[sz][i]=-1; last[sz++]=false; return sz-1; } void init() { sz=0; root=newnode(); } void insert(char s[]) { int lens=strlen(s); int now=root; for(int i=0;i<lens;i++) { int id=mp[s[i]]; if(ch[now][id]==-1) { ch[now][id]=newnode(); } now=ch[now][id]; } last[now]=true; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<256;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); if(last[fail[now]])last[now]=true; for(int i=0;i<256;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } matrix getmat(int n) { matrix res=matrix(sz); for(int i=0;i<sz;i++) for(int j=0;j<n;j++) if(!last[ch[i][j]])res.m[i][ch[i][j]]++; return res; } }ac; char buf[1010]; BigInt dp[2][110]; int main() { int n,m,p; while(scanf("%d%d%d",&n,&m,&p)>0) { getchar(); gets(buf); mp.clear(); int len = strlen(buf); for(int i = 0;i < len;i++) mp[buf[i]]=i; ac.init(); for(int i = 0;i < p;i++) { gets(buf); ac.insert(buf); } ac.build(); matrix a= ac.getmat(n); int now=0; dp[now][0]=1; for(int i=1;i<a.n;i++)dp[now][i]=0; for(int i=1;i<=m;i++) { now^=1; for(int j=0;j<a.n;j++)dp[now][j]=0; for(int j=0;j<a.n;j++) for(int k=0;k<a.n;k++) if(a.m[j][k])dp[now][k]=dp[now][k]+dp[now^1][j]*a.m[j][k]; } BigInt ans = 0; for(int i = 0;i < a.n;i++) ans = ans + dp[now][i]; ans.output(); } return 0; }
2.HDU 2825
题意:给出字符串(<10),求至少由x个字符串组成的长度为n的字符串种数。
分析:dp[i][j][k]表示长度为i时,以自动机上结点编号为j结尾,把用到的串二进制压缩为k的种数。dp[i][j][k]+=dp[i-1][p][t],p是自动机上能转移到j的结点。(dp[0][0][0]=1)
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <vector> using namespace std; const int mod = 20090717; const int maxn = 103; const int max_num = 26; int idx[256]; int n, m, x; int dp[26][103][1026]; bool vis[26][103][1026]; int cnt[1025]; struct node { int v, p, zt; node(){} node(int v, int p, int zt) : v(v), p(p), zt(zt){} }Q[1000006]; struct AcAuto { int val[maxn], f[maxn]; int ch[maxn][max_num], tot; void init() { tot = 0; new_node(); int i; for(i = 0; i < 26; i++) idx['a'+i] = i; } inline int new_node() { memset(ch[tot], 0, sizeof(ch[tot])); val[tot] = 0; f[tot] = 0; return tot++; } void insert(char *s, int id) { int i, j, p = 0; for(;*s; s++) { int k = idx[*s]; if(!ch[p][k]) ch[p][k] = new_node(); p = ch[p][k]; } val[p] |= 1<<id; } void getfail() { int i, j, p = 0; int q[maxn]; int *s = q, *e = q; for(i = 0; i < max_num; i++) if(ch[0][i]) *e++ = ch[0][i]; while(s != e) { int u = *s++; for(i = 0; i < max_num; i++) { int &v = ch[u][i]; if(!v) { v = ch[f[u]][i]; continue; } *e++ = v; j = f[u]; while(j && !ch[j][i]) j = f[j]; f[v] = ch[j][i]; val[v] |= val[f[v]]; } } } void solve() { int i, j, k, u; int M = (1<<m); for(i = 0; i <= n; i++) for(k = 0; k < tot; k++) for(j = 0; j < M; j++) dp[i][k][j] = 0; dp[0][0][0] = 1; node *s = Q, *e = Q; *e++ = node(0, 0, 0); vis[0][0][0] = 1; while(s != e) { node u = *s++; vis[u.v][u.p][u.zt] = 0; if(u.v >= n) continue; for(i = 0; i < max_num; i++) { int p = ch[u.p][i]; node v = node(u.v+1, p, u.zt|val[p]); dp[v.v][v.p][v.zt] += dp[u.v][u.p][u.zt]; if(dp[v.v][v.p][v.zt] >= mod) dp[v.v][v.p][v.zt] -= mod; if(!vis[v.v][v.p][v.zt]) { vis[v.v][v.p][v.zt] = 1; *e++ = v; } } } int ans = 0; for(i = 0; i < M; i++) { if(cnt[i] >= x) for(j = 0; j < tot; j++) { ans += dp[n][j][i]; if(ans >= mod) ans -= mod; } } printf("%d\n", ans); } }AC; char str[13]; int main() { int i, j; for(i = 0; i < 1024; i++) { int c = 0; for(j = i; j; j -= (j&-j)) c++; cnt[i] = c; } while( ~scanf("%d%d%d", &n, &m, &x) && (n || m || x)) { AC.init(); for(i = 0; i < m; i++) { scanf("%s", str); AC.insert(str, i); } AC.getfail(); AC.solve(); } return 0; }
题意:给定n个字符的花费代价,m个字符串的获取价值及现有的总费用B,求在费用B范围内构造一个拥有最大价值的字符串(包含几个价值字符串获得多大价值)。
分析:构建trie图获取fail指针时顺便总计到达每个节点得到的总价值,及每个节点都加上它fail指针指向的节点价值,然后进行dp。
dp[i][j]表示到达第i节点时花费j得到最大总价值。
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <map> using namespace std; const int N = 10010; struct Trie { int ch[N][26],fail[N],val[N]; int root,sz; int newnode() { for(int i=0; i<26; i++)ch[sz][i]=-1; val[sz++]=0; return sz-1; } void init() { sz=0; root=newnode(); } void insert(char s[],int c) { int len=strlen(s); int now=root; for(int i=0; i<len; i++) { int id=s[i]-'A'; if(ch[now][id]==-1)ch[now][id]=newnode(); now=ch[now][id]; } val[now]+=c; } void build() { queue<int>que; fail[root]=root; for(int i=0; i<26; i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front(); que.pop(); val[now]+=val[fail[now]]; for(int i=0; i<26; i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } } ac; char s[110]; int c[30],dp[N][210]; int main() { int T,n,m,b,x,cas=1; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&b); memset(c,0,sizeof(c)); for(int i=0; i<n; i++) { scanf("%s%d",s,&x); c[s[0]-'A']=x; } ac.init(); for(int i=0; i<m; i++) { scanf("%s%d",s,&x); ac.insert(s,x); } ac.build(); memset(dp,-1,sizeof(dp)); dp[0][0]=0; int ans=0; for(int k=0; k<b; k++) { for(int i=0; i<ac.sz; i++) { if(dp[i][k]==-1)continue; for(int j=0; j<26; j++) { if(!c[j]||k+c[j]>b)continue; int now=ac.ch[i][j],w=c[j]; dp[now][k+w]=max(dp[now][k+w],dp[i][k]+ac.val[now]); ans=max(ans,dp[now][k+w]); } } } printf("Case #%d: %d\n",cas++,ans); } }
4.HDU 2457
题意:给出病毒串(<50),求最少的修改次数,使得DNA串不含病毒串。
分析:dp[i][j]表示长度为i时,以自动机上结点编号为j结尾,使得该DNA串不含病毒串的最小修改次数。每次转移枚举是否需要修改,以及需要修改的字符。
#include <cstdio> #include <cstring> #include <cmath> #include <queue> #include <algorithm> using namespace std; const int N = 1050; const int inf = 0x3f3f3f3f; int dp[N][N]; int n; struct Trie { int ch[N][4],fail[N]; bool last[N]; int root,sz; int newnode() { for(int i=0;i<4;i++)ch[sz][i]=-1; last[sz++]=false; return sz-1; } void init() { sz=0; root=newnode(); } int getid(char ch) { if(ch=='A')return 0; else if(ch=='C')return 1; else if(ch=='G')return 2; else return 3; } void insert(char s[]) { int len=strlen(s); int now=root; for(int i=0;i<len;i++) { int id=getid(s[i]); if(ch[now][id]==-1)ch[now][id]=newnode(); now=ch[now][id]; } last[now]=true; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<4;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); if(last[fail[now]])last[now]=true; for(int i=0;i<4;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } int solve(char s[]) { int len=strlen(s); for(int i=0;i<=len;i++) for(int j=0;j<sz;j++)dp[i][j]=inf; dp[0][0]=0; for(int i=0;i<len;i++) { int t=getid(s[i]); for(int j=0;j<sz;j++) { if(dp[i][j]>=inf)continue; for(int k=0;k<4;k++) { int now=ch[j][k]; if(last[now])continue; dp[i+1][now]=min(dp[i+1][now],dp[i][j]+(t!=k)); } } } int ans=inf; for(int i=0;i<sz;i++) ans=min(ans,dp[len][i]); return ans==inf?-1:ans; } }ac; char s[N]; int main() { int cas=1; while(scanf("%d",&n),n) { ac.init(); for(int i=0;i<n;i++) { scanf("%s",s); ac.insert(s); } ac.build(); scanf("%s",s); printf("Case %d: %d\n",cas++,ac.solve(s)); } }
4.HDU 3247
题意:有n(<10)个01串以及m(<1000)个病毒串,将01串拼接成最短的且不含病毒串。
分析:将01串和病毒串一起构造自动机,枚举每个01串的结尾广搜,可以得到该串和其他01串结尾的最短距离。
对n个01串二进制压缩,即TSP,dp[i][j]表示状态为i,最后拼接上的01串是第j个。这题要先排除各个串相互
为子串的情况,否则得到的必定不是最优答案。这题的数据太弱了,网上很多代码都过不了这几组数据也能AC.
Input:
3 1
00000
00000
11111
01
3 2
101
010
1111
001
011
2 2
1110
0111
101
1001
3 3
0001
0000
10000
010
101
111
3 3
00000
00000
00000
101
101
101
OutPut:
10
7
5
6
5
#include <cstdio> #include <cstring> #include <string> #include <iostream> #include <queue> #include <algorithm> using namespace std; const int N = 200010; const int inf = 0x3f3f3f3f; int dp[1<<15][15]; string s[15]; struct Trie { int ch[N][2],fail[N],last[N],len[N],d[N],pos[15],dis[15][15]; int root,sz,cnt; int newnode() { for(int i=0;i<2;i++)ch[sz][i]=-1; last[sz++]=0; return sz-1; } void init() { sz=0;cnt=0; root=newnode(); } void insert(string str,int id) { int len=str.size(); int now=root; for(int i=0;i<len;i++) { int id=str[i]-'0'; if(ch[now][id]==-1)ch[now][id]=newnode(); now=ch[now][id]; } last[now]=id; } void build() { queue<int>que; fail[root]=root; for(int i=0;i<2;i++) { if(ch[root][i]==-1)ch[root][i]=root; else { fail[ch[root][i]]=root; que.push(ch[root][i]); } } while(!que.empty()) { int now=que.front();que.pop(); if(last[fail[now]]==-1)last[now]=-1; for(int i=0;i<2;i++) { if(ch[now][i]==-1)ch[now][i]=ch[fail[now]][i]; else { fail[ch[now][i]]=ch[fail[now]][i]; que.push(ch[now][i]); } } } } bool vis[N]; void bfs(int u,int k) { memset(vis,false,sizeof(vis)); memset(d,-1,sizeof(d)); queue<int>que; d[u]=0;vis[u]=true; que.push(u); while(!que.empty()) { int now=que.front();que.pop(); for(int i=0;i<2;i++) { int nxt=ch[now][i]; if(last[nxt]!=-1&&!vis[nxt]) { vis[nxt]=true; d[nxt]=d[now]+1; que.push(nxt); } } } for(int i=0;i<cnt;i++)dis[k][i]=d[pos[i]]; } int solve(int n) { for(int i=0;i<(1<<n);i++) for(int j=0;j<n;j++) dp[i][j]=inf; for(int i=0;i<sz;i++) if(last[i]>0)pos[cnt]=i,len[cnt++]=s[last[i]].size(); for(int i=0;i<cnt;i++)bfs(pos[i],i); int ans=inf; for(int s=0;s<(1<<cnt);s++) { for(int i=0;i<cnt;i++) { if(s&(1<<i)) { if(s==1<<i)dp[s][i]=len[i]; for(int j=0;j<cnt;j++) { if(i==j||(s&(1<<j))||dis[i][j]==-1)continue; dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+dis[i][j]); } } } } for(int i=0;i<cnt;i++)ans=min(dp[(1<<cnt)-1][i],ans); return ans; } }ac; int n,m; int cmp(string s1,string s2) { return s1.size()>s2.size(); } void solve_sub() { int tmp=n,k,vis[15]; sort(s+1,s+n+1,cmp); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(s[i].find(s[j])!=-1)vis[j]=1; for(n=0,k=1;k<=tmp;k++) { if(!vis[k])s[++n]=s[k]; } } int main() { while(scanf("%d%d",&n,&m)>0) { if(n+m==0)break; ac.init(); for(int i=1;i<=n;i++) { cin>>s[i]; } solve_sub(); for(int i=1;i<=n;i++)ac.insert(s[i],i); for(int i=0;i<m;i++) { cin>>s[0]; ac.insert(s[0],-1); } ac.build(); printf("%d\n",ac.solve(n)); } }