很有意思的一道题,这次在dp的基础上加了修改,要求最少改几次。
dp[i][j]表示当前处理到了第i个字符,所在节点为j,需要修改的次数。
注意到一件事情,dp[i][j]可以指代原串的前i位经过修改,到达了字典树上的j节点。在dp状态转移时,我们要保证修改后的串不含输入的若干字符串,所以这些串结尾对应的节点我们是不能走的。其他节点都可以走,那么当原串的i+1位与我们要放的字符k不一样时,dp值+1,就可以视作进行了一次修改,到达了ch[j][k]节点了。当然如果原本就没有包含输入的字符串,不修改的状态也会被转移。最后dp[n][i]中的最小值就是答案了。
#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <cstring>
#define fi first
#define se second
#define FIN freopen("in.txt","r",stdin)
#define FIO freopen("out.txt","w",stdout)
#define INF 0x3f3f3f3f
#define per(i,a,n) for(int i = a;i < n;i++)
#define rep(i,a,n) for(int i = n;i > a;i--)
#define pern(i,a,n) for(int i = a;i <= n;i++)
#define repn(i,a,n) for(int i = n;i >= a;i--)
#define fastio std::ios::sync_with_stdio(false)
#define all(a) a.begin(), a.end()
#define ll long long
#define pb push_back
#define endl "\n"
#define pii pair<int,int>
#define sc(n) scanf("%d", &n)
#define CASET int ___T; scanf("%d", &___T); for(int cs=1;cs<=___T;cs++)
template<typename T> inline void _max(T &a,const T b){
if(a<b) a = b;}
template<typename T> inline void _min(T &a,const T b){
if(a>b) a = b;}
using namespace std;
//inline ll read(){
// ll a=0;int f=0;char p=getchar();
// while(!isdigit(p)){f|=p=='-';p=getchar();}
// while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();}
// return f?-a:a;
//}
const int maxn = 2000;
const int maxnode = 4;
int ch[maxn][maxnode]; //字典树
int cnt[maxn]; //单词出现次数
int sz;
int fail[maxn];
map<char,int> mp;
void init()
{
mp['A'] = 0;
mp['G'] = 1;
mp['C'] = 2;
mp['T'] = 3;
sz = 1;
memset(ch[0], 0, sizeof(ch[0]));
memset(cnt,0,sizeof(cnt));
cnt[0] = 0;
}
void insert(char str[], int len) //插入字符串
{
int u = 0;
per(i, 0, len)
{
int v = mp[str[i]];
if (!ch[u][v])
{
memset(ch[sz], 0, sizeof(ch[sz]));
cnt[sz] = 0;
ch[u][v] = sz++;
}
u = ch[u][v];
}
cnt[u]=1;
//在这里我们可以建立一个int-string的映射,以通过节点序号得知这个点是哪个单词的结尾
}
void getfail()
{
//所有模式串已插入完成
queue<int> q;
per(i, 0,maxnode)
{
if (ch[0][i])
{
fail[ch[0][i]] = 0;
q.push(ch[0][i]);
}
}
while (!q.empty())
{
int now = q.front();
q.pop();
per(i, 0, maxnode)
{
if (ch[now][i])
{
fail[ch[now][i]] = ch[fail[now]][i];
q.push(ch[now][i]);
}
else
ch[now][i] = ch[fail[now]][i];
}
cnt[now] |= cnt[fail[now]];
}
}
char s[1000+100];
ll dp[1010][maxn];
//string path[55][maxn];
int main()
{
#ifndef ONLINE_JUDGE
int startTime = clock();
FIN;
#endif
//fastio;
//忘记初始化是小狗
//freopen("out.txt","w",stdout);
//ios::sync_with_stdio(false);
int n;
//cout << id('0');
int t = 0;
while(~sc(n))
{
if(n == 0)return 0;
//scanf("%d%d",&n,&m);
//if(n==0)return 0;
init();
per(i,0,n)
{
scanf("%s",s);
insert(s,strlen(s));
}
scanf("%s",s);
int len = strlen(s);
getfail();
memset(dp,0x3f,sizeof(dp));
dp[0][0] = 0;
per(i,1,len+1)
{
per(j,0,sz)
{
per(k,0,4)
{
int v = ch[j][k];
if(cnt[v] == 0) dp[i][v] = min(dp[i][v],dp[i-1][j]+(k!=(mp[s[i-1]])));
}
}
}
ll ans = INF;
per(i,0,sz) ans = min(ans,dp[len][i]);
printf("Case %d: %d\n", ++t, ans == INF ? -1 : ans);
}
#ifndef ONLINE_JUDGE
printf("\nTime = %dms\n", clock() - startTime);
#endif
return 0;
}