版权声明:本文为博主原创文章,请随意转载(注明出处)。 https://blog.csdn.net/can919/article/details/81584628
题意
给定N个模式字符串,和一个匹配串,要求匹配串中不出现任何一个模式串,最少修改几个字母?
(字符串中只有’A’、’C’、’G’、’T’,修改操作也只能修改成这四个字母)
题解
简单的AC自动机+DP,适合作为模板题
(不知道这中算法的人)首先想到的就是最暴力的DP就是:dp[i][j]表示当前在第i位,第i位为j字符时,最少需要修改多少次,每次转移到i就必须检查i之前的若干位是否为模式串;
而AC自动机就能优化这一缺点
将dp定义为dp[i][j]表示当前在第i位,匹配到AC自动机上的第j个结点
转移时,考虑添加一个字符,在AC自动机上获取添加这个结点会转移到的下一个结点(字符串匹配),并判断这样转移是否形成了一个模式串。
dp[i+1][u->由字符k转移到 v]=dp[i][u]+(字符k == str[i+1])
代码
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define charID(x) (x=='A'?0:(x=='G'?1:(x=='C'?2:3)))
const int MAXN=53,MAXL1=22,MAXL2=1005,MAXCHAR=4;
//dp[i][j]+ * -> dp[i+1][nxt[j]['A''G''C''T']]
struct Node
{
int val;
Node *son[MAXCHAR],*fail;
void Clear()
{memset(this,0,sizeof(Node));}
};
Node nodes[MAXN*MAXL1],*root=nodes,*ntop=nodes+1;
void Clear()
{
root=nodes;
root->Clear();
ntop=nodes+1;
}
int ID(Node *u)
{return u-nodes;}
void Insert(const char *str)
{
Node *u=root;
for(int i=0;str[i];i++)
{
int c=charID(str[i]);
if(u->son[c]==NULL)
{
u->son[c]=ntop++;
u->son[c]->Clear();
}
u=u->son[c];
}
u->val=1;
}
void Build()
{
static queue<Node*> Q;
for(int i=0;i<MAXCHAR;i++)
if(root->son[i])
{
root->son[i]->fail=root;
Q.push(root->son[i]);
}
while(!Q.empty())
{
Node *u=Q.front();
Q.pop();
for(int i=0;i<MAXCHAR;i++)
if(u->son[i])
{
Node *v=u->son[i],*f=u->fail;
Q.push(v);
while(f&&f->son[i]==NULL)
f=f->fail;
if(f)
v->fail=f->son[i];
else
v->fail=root;
v->val|=v->fail->val;
}
}
}
Node *GetNext(Node *u,int c)
{
while(u&&u->son[c]==NULL)
u=u->fail;
if(u)
return u->son[c];
return root;
}
char s[MAXL1],str[MAXL2];
int dp[MAXL2][MAXN*MAXL1];
int main()
{
for(int test=1;;test++)
{
int n,m;
scanf("%d",&n);
if(n==0)
break;
Clear();
for(int i=1;i<=n;i++)
{
scanf("%s",s);
Insert(s);
}
Build();
scanf("%s",str+1);
m=strlen(str+1);
memset(dp,0x3F,sizeof dp);
dp[0][0]=0;
for(int i=0;i<m;i++)
for(int j=0;nodes+j<ntop;j++)
if(dp[i][j]<0x3F3F3F3F)
{
Node *u=nodes+j;
for(int c=0;c<MAXCHAR;c++)
{
Node *v=GetNext(u,c);
if(v->val)
continue;
dp[i+1][ID(v)]=min(dp[i+1][ID(v)],dp[i][j]+(c!=charID(str[i+1])));
}
}
int ans=0x3F3F3F3F;
for(int j=0;nodes+j<ntop;j++)
ans=min(ans,dp[m][j]);
if(ans==0x3F3F3F3F)
ans=-1;
printf("Case %d: %d\n",test,ans);
}
return 0;
}