题目的意思是给你一些串,然后给你一个大串,问你要在这个大串上最少修改多少个地方使得这个大串不含任何一个小的串。
分析:经典套路,直接dp,还是设f[i,j]表示走到大串的第i个位置,然后直接把模板串往trie上贴就行了,如果当前位置trie上的字母和模板串相同自然不用修改,否则就+1咯。当然不能走单词的末尾节点,直接跳过就可以了。最后注意一下边界,还有如果你是直接读串的话c++是dp的状态和子串的位置刚好差了一位,就s[i]对应的状态是f[i+1][]..
忘记清空trie结果改了几个小时。。醉了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n,m;
const int N=1e4;
const int inf=1e9;
int tot,val[N],cas,f[3005][3005],fail[N],mp[N],a[N][30],q[N];
char s[N];
inline void ins()
{
int x=0,len=strlen(s);
fo(i,0,len-1)
{
int c;
if (s[i]=='A')c=0;
else if (s[i]=='T')c=1;
else if (s[i]=='C')c=2;
else if (s[i]=='G')c=3;
if (!a[x][c])a[x][c]=++tot;
x=a[x][c];
}
val[x]=1;
}
inline void getfail()
{
int t=0,w=0;
fo(i,0,3)if (a[0][i])q[w++]=a[0][i];
while (t<w)
{
int x=q[t++];
fo(i,0,3)
if (a[x][i])
{
int k=fail[x];
while (k&&!a[k][i])k=fail[k];
k=a[k][i];
fail[a[x][i]]=k;
if (val[k])val[a[x][i]]=1;
q[w++]=a[x][i];
}
else a[x][i]=a[fail[x]][i];
}
}
inline void dp()
{
m=strlen(s);
memset(f,127,sizeof(f));
f[0][0]=0;
fo(i,0,m-1)
{
fo(j,0,tot)
if (f[i][j]<inf)
{
int k=a[j][mp[s[i]]];
fo(k,0,3)
{
int v=a[j][k];
if (val[v])continue;
if (mp[s[i]]==k)
f[i+1][v]=min(f[i+1][v],f[i][j]);
else f[i+1][v]=min(f[i+1][v],f[i][j]+1);
}
}
}
int ans=inf;
fo(i,0,tot)
ans=min(ans,f[m][i]);
if (ans==inf) printf("Case %d: -1\n",cas++);
else
printf("Case %d: %d\n",cas++,ans);
}
int main()
{
cas=1;
mp['A']=0;
mp['T']=1;
mp['C']=2;
mp['G']=3;
while (1)
{
scanf("%d",&n);
memset(a,0,sizeof(a));
memset(val,0,sizeof(val));
memset(fail,0,sizeof(fail));
if (n==0)break;
tot=0;//就这句害我改半天。
fo(i,1,n)
{
scanf("%s",s);
ins();
}
getfail();
scanf("%s",s);
dp();
}
return 0;
}