DNA Repair
考虑只由’A’,’G’,’C’,’T’四种字符组成的 DNA字符串。给定一个原字符串 S,和 n个禁止模式字符串
p1,p2,..,pn。请修改字符串 S,使得其中不包含任何禁止模式。
每次修改操作只能将 S中的某个字符修改为其他字符。
如果不存在这样的修改,请输出 -1,否则,输出所需的最少修改回数。
思路:
这个问题看起来与前面的问题很相似。所以我们考虑从左向右修改,依然以已修改的位置和对应的后缀为状态进行动态规划。只不过与前面不同的是,这里禁止出现的字符串不只一个,而是有多个。那么应该用哪些状态来表示后缀才好呢?
实际上,这并不难。在前面的问题中,我们取字符串S的所有前缀为状态,这里也只要取所有字符串Pi的所有前缀为状态就好了。不过需要注意的是,我们可能从不同的字符串得到相同的前缀。譬如说,“AA”既是“AAA”的前缀,也是“AAG”的前缀。在“AA”后面添加‘A’可以得到”AAA“,添加’G’则会得到”AAG“。因此,需要把像”AA“这样的多个字符串的公共前缀作为同一个状态进行正确的处理。
与前面的问题一样,下面的代码中,首先是预处理出状态及其转移关系,然后再进行动态规划。预处理的复杂度O(n^2*l^2+n*l^3log(nl)),动态规划的复杂度是O(nl|S|),其中l表示Pi的最大长度
/*
DNA Repair
考虑只由'A','G','C','T'四种字符组成的 DNA字符串。给定一个原字符串 S,和 n个禁止模式字符串
p1,p2,..,pn。请修改字符串 S,使得其中不包含任何禁止模式。
每次修改操作只能将 S中的某个字符修改为其他字符。
如果不存在这样的修改,请输出 -1,否则,输出所需的最少修改回数。
*/
#include<iostream>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
const char *AGCT = "AGCT";
const int MAX_N = 100;
const int MAX_STATE = 1005;
const int MAX_LEN_S = 1005;
const int INF = 99999999;
//输入
int N;
string S,P[MAX_N];
//预处理得到的数据
int next[MAX_STATE][4];//添加某个字符后转移得到的状态
bool ng[MAX_STATE];//是否是禁止转移到的状态
int dp[MAX_LEN_S + 1][MAX_STATE];
void solve(){
//首先枚举出所有的字符串前缀
vector<string> pfx;
for(int i=0;i<N;i++){
for(int j=0;j<=P[i].length();j++){
pfx.push_back(P[i].substr(0,j));
}
}
//排序并去重
sort(pfx.begin(),pfx.end());
pfx.erase(unique(pfx.begin(),pfx.end()),pfx.end());
int K = pfx.size();
//计算各个状态的相关信息
for(int i=0;i<K;i++){
//如果后缀和禁止模式匹配的话,就是禁止转移到的状态
ng[i] = false;
for(int j=0;j<N;j++){
ng[i] |= P[j].length() <= pfx[i].length()
&& pfx[i].substr(pfx[i].length() - P[j].length(),P[j].length()) == P[j];
}
for(int j=0;j<4;j++){
//添加一个字符后得到的字符串
string s = pfx[i] + AGCT[j];
//反复删除第一个字符,直到等于某个状态的字符串,该状态就是状态转移到的状态
int k;
for(;;){
k = lower_bound(pfx.begin(),pfx.end(),s) - pfx.begin();
if(k < K && pfx[k] == s)
break;
s = s.substr(1);
}
next[i][j] = k;
}
}
//动态规划的边界初值
dp[0][0] = 1;
for(int i=0;i<K;i++)
dp[0][i] = 0;
//动态规划
for(int t=0;t<S.length();t++){
for(int i=0;i<K;i++)
dp[t+1][i] = INF;
for(int i=0;i<K;i++){
if(ng[i])
continue;
for(int j=0;j<4;j++){
int k = next[i][j];
dp[t+1][k] = min(dp[t+1][k],dp[t][i] + (S[t] == AGCT[j] ? 0 : 1));
}
}
}
int ans = INF;
for(int i=0;i<K;++i){
if(ng[i])
continue;
ans = min(ans,dp[S.length()][i]);
}
if(ans == INF)
puts("-1");
else
printf("%d\n",ans);
}
int main(){
cin>>S;
cin>>N;
for(int i=0;i<N;i++){
cin>>P[i];
}
solve();
return 0;
}