题意:
给出n个非法的串,然后给出一个目标串,问如何改变目标串使得用最少步数让目标串不包含非法串。
题解:
dp[i][j]第i个字母结尾点在节点j处的最小处理操作。ac自动机上的dp很难解释,我也能意会,功力不够。大概就是字典图上的转移方程,将字典图与目标串进行匹配dp。注意fail指针的修改,这算是最难理解的部分。
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const ll MOD=20090717;
const int maxn=1005;
const int SIZE=1005;
const int alph=4;
char str[maxn];
int dp[maxn][SIZE];
map<char,int>mat;
struct AC
{
int next[SIZE][alph],fail[SIZE],end[SIZE],Q[SIZE*alph];
int root,cnt;
void Init()
{
cnt=0;
root=newNode();
}
int newNode()
{
for(int i=0;i<alph;i++)
next[cnt][i]=-1;
end[cnt++]=0;
return cnt-1;
}
void Insert(char buff[])
{
int now=root;
int len=strlen(buff);
for(int i=0,k;i<len;i++)
{
k=mat[buff[i]];
if(next[now][k]==-1)
next[now][k]=newNode();
now=next[now][k];
}
end[now]=1;
}
void build()
{
fail[root]=root;
int front,rear;
front=rear=0;
int now=root;
for(int i=0;i<alph;i++)
{
if(next[now][i]==-1)
next[now][i]=root;
else
{
fail[next[now][i]]=root;
Q[rear++]=next[now][i];
}
}
while(front<rear)
{
now=Q[front++];
if(end[fail[now]]) end[now]=1;
for(int i=0;i<alph;i++)
{
if(next[now][i]==-1)
next[now][i]=next[fail[now]][i];
else
{
fail[next[now][i]]=next[fail[now]][i];
Q[rear++]=next[now][i];
}
}
}
}
void cmin(int& a,int b)
{
if(b<a)
a=b;
}
int DP(int n)
{
memset(dp,0x3f,sizeof dp);
dp[0][0]=0;///root==0;
for(int i=0;i<n;i++)
{
for(int j=0;j<cnt;j++)
if(dp[i][j]!=oo)
{
int temp;
for(int a=0;a<4;a++)
{
int k=next[j][a];
if(end[k])continue;
if(mat[str[i]]==a) temp=dp[i][j];
else temp=dp[i][j]+1;
cmin(dp[i+1][k],temp);
}
}
}
int ans=oo;
for(int i=0;i<cnt;i++)
cmin(ans,dp[n][i]);
return ans;
}
};
AC ac;
int main()
{
int n,cas=1,len;
mat['A']=0;
mat['G']=1;
mat['C']=2;
mat['T']=3;
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
ac.Init();
for(int i=1;i<=n;i++)
{
scanf("%s",str);
ac.Insert(str);
}
scanf("%s",str);
len=strlen(str);
ac.build();
int ans=ac.DP(len);
if(ans>=oo)ans=-1;
printf("Case %d: %d\n",cas++,ans);
}
return 0;
}
/**
2
AAA
AAG
AAAG
2
A
TG
TGAATG
4
A
G
C
T
AGT
0
*/