poj 3691
ac自动机+dp 自动机上的节点来作为状态
dp[i][j]表示长度为i状态为j至少需要转换多少个字符
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int root=0,N=4; char a[1111]; int dp[1111][1111],c[1111][10]; struct { int next[5],tmp,fail; }trie[1111]; int lon; void trieini() { memset(trie,0,sizeof(trie)); lon=0; } void insert(char s[]) { int t=root; int n=strlen(s+1); for(int i=1;i<=n;i++) { if(trie[t].next[s[i]-'A']==0) trie[t].next[s[i]-'A']=++lon; t=trie[t].next[s[i]-'A']; if(i==n) trie[t].tmp++; } } void getfail() { queue <int> q; q.push(root); while(!q.empty()) { int t=q.front(); q.pop(); for(int i=0;i<N;i++) if(trie[t].next[i]) { int u=trie[t].next[i]; int tmp=trie[t].fail; while(tmp!=root&&trie[tmp].next[i]==0) tmp=trie[tmp].fail; trie[u].fail=trie[tmp].next[i]; if(t==root) trie[u].fail=root; trie[u].tmp+=trie[trie[u].fail].tmp; q.push(u); } } } void find() { for(int i=0;i<=lon;i++) { for(int k=0;k<N;k++) { int t=i; while(t&&trie[t].next[k]==0) t=trie[t].fail; if(trie[t].next[k]) c[i][k]=trie[t].next[k]; else c[i][k]=root; } } } void work(char a[]) { int n=strlen(a+1); for(int i=1;i<=n;i++) { if(a[i]=='T') a[i]='B'; else if(a[i]=='G') a[i]='D'; } } int main() { int n; int tcase=0; while(scanf("%d",&n),n) { trieini(); for(int i=1;i<=n;i++) { char s[22]; scanf("%s",s+1); work(s); insert(s); } getfail(); find(); scanf("%s",a+1); work(a); memset(dp,50,sizeof(dp)); dp[0][0]=0; int m=strlen(a+1); for(int k=0;k<m;k++) for(int i=0;i<=lon;i++) if(trie[i].tmp==0) { for(int p=0;p<N;p++) { int tmp=p+'A'!=a[k+1]; dp[k+1][c[i][p]]=min(dp[k+1][c[i][p]],dp[k][i]+tmp); } } int ans=1111; for(int i=0;i<=lon;i++) if(trie[i].tmp==0) { ans=min(ans,dp[m][i]); // printf("%d %d %d\n",m,i,dp[m][i]); } printf("Case %d: ",++tcase); if(ans==1111) printf("-1\n"); else printf("%d\n",ans); } return 0; }