HDU 2457 DNA repair(AC自动机+DP)
http://acm.hdu.edu.cn/showproblem.php?pid=2457
题意:
给你N个模板串,并且给你一个文本串,现在问你这个文本串最少需要改变几个字符才能使得它不包含任何模板串.(以上字符只由A,T,G,C构成).
分析:
本题初看确实不知道怎么做,和之前做的题目都不一样.
现在我们令dp[i][j]=x表示当前在自动机的i号节点,且走了长为j的路了,且过程中都不经过单词节点的情况下,与文本串的前j个字符不同点最少有x个.(仔细体会dp[i][j]的定义).
初值dp[i][j]=-1,dp[0][0]=0.
dp[i][j] = min( dp[k][j-1] + v ) 从k可以走到i,且i和k都是非后缀单词.
设从k走到i的那条边的字符为x,如果x==s[j-1],那么v=0.否则v=1.
AC代码:1A
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
using namespace std;
const int maxnode=1000+100;
const int sigma_size=4;
map<char,int> mp;
char sigma[4];
int N,M;//M为文本串的长度
char str[maxnode];//文本串
struct AC_Automata
{
int ch[maxnode][sigma_size];
int f[maxnode];
int match[maxnode];
int dp[maxnode][maxnode];
int sz;
void init()
{
sz=1;
memset(ch[0],0,sizeof(ch[0]));
match[0]=f[0]=0;
}
void insert(char *s)
{
int n=strlen(s),u=0;
for(int i=0;i<n;i++)
{
int id=mp[s[i]];
if(ch[u][id]==0)
{
ch[u][id]=sz;
memset(ch[sz],0,sizeof(ch[sz]));
match[sz++]=0;
}
u=ch[u][id];
}
match[u]=1;
}
void getFail()
{
queue<int> q;
for(int i=0;i<sigma_size;i++)
{
int u=ch[0][i];
if(u)
{
f[u]=0;
q.push(u);
}
}
while(!q.empty())
{
int r=q.front();q.pop();
for(int i=0;i<sigma_size;i++)
{
int u=ch[r][i];
if(!u){ ch[r][i]=ch[f[r]][i]; continue; }
q.push(u);
int v=f[r];
while(v && ch[v][i]==0) v=f[v];
f[u]=ch[v][i];
match[u] |= match[f[u]];
}
}
}
int solve()
{
int ans=-1;
for(int i=0;i<sz;i++)
for(int j=0;j<=M;j++)
dp[i][j]=-1;
dp[0][0]=0;
for(int len=0;len<M;len++)
for(int i=0;i<sz;i++)if(match[i]==0 && dp[i][len] != -1)
for(int j=0;j<sigma_size;j++)if(match[ch[i][j]]==0)
{
int k=ch[i][j];
int v=(str[len]==sigma[j])? 0:1;
if(dp[k][len+1] != -1)dp[k][len+1] = min(dp[k][len+1] , dp[i][len]+v);
else dp[k][len+1] = dp[i][len]+v;
if(len+1==M)
{
if(ans != -1) ans = min(ans , dp[k][len+1]);
else ans = dp[k][len+1];
}
}
return ans;
}
};
AC_Automata ac;
int main()
{
mp['A']=0,mp['G']=1,mp['C']=2,mp['T']=3;
sigma[0]='A',sigma[1]='G',sigma[2]='C',sigma[3]='T';
int kase=0;
while(scanf("%d",&N)==1&&N)
{
ac.init();
while(N--)
{
scanf("%s",str);
ac.insert(str);
}
scanf("%s",str);
M=strlen(str);
ac.getFail();
printf("Case %d: %d\n",++kase,ac.solve());
}
return 0;
}