题目来源:HDU 2457 DNA repair
题意:给你一个文本串 求最少需要修改的次数 使得文本串不包含模式串
思路:可以一位一位构造一个和文本串长度一样的字符串 就4个字符 每次4选1 如果对应的位置的字符不一样修改的次数+1 并且构造的时候不包含禁止节点
dp[i][j] 代表到文本串的前i位 在ac自动机的状态为j 最少需要修改的次数
dp[l+1][v] = min(dp[l+1][v], dp[l][i] + sum); sum的值为0或1 每次都是当前状态推后面的状态 v 是i的后续状态 v 和 i都不是禁止节点的状态
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxnode = 55*22;
const int size = 4;
int ch[maxnode][size];
int val[maxnode];
int f[maxnode];
int sz;
const int maxn = 1010;
int dp[maxn][maxnode];
char s[maxn];
void init()
{
sz = 1;
memset(ch[0], 0, sizeof(ch[0]));
}
int idx(char c)
{
if(c == 'A')
return 0;
if(c == 'T')
return 1;
if(c == 'C')
return 2;
if(c == 'G')
return 3;
}
void insert(char *s, int v)
{
int u = 0, n = strlen(s);
for(int i = 0; i < n; i++)
{
int c = idx(s[i]);
if(!ch[u][c])
{
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = 1;
}
void getFail()
{
queue <int> q;
f[0] = 0;
for(int c = 0; c < 4; c++)
{
int u = ch[0][c];
if(u)
{
f[u] = 0;
q.push(u);
}
}
while(!q.empty())
{
int r = q.front(); q.pop();
for(int c = 0; c < 4; c++)
{
int u = ch[r][c];
if(!u)
{
ch[r][c] = ch[f[r]][c];
continue;
}
q.push(u);
int v = f[r];
while(v && !ch[v][c])
v = f[v];
f[u] = ch[v][c];
val[u] |= val[f[u]];
}
}
}
int main()
{
int cas = 1;
int n;
while(scanf("%d", &n) && n)
{
init();
for(int i = 0; i < n; i++)
{
char s[22];
scanf("%s", s);
insert(s, 1);
}
getFail();
scanf("%s", s);
memset(dp, -1, sizeof(dp));
int ans = 999999999;
dp[0][0] = 0;
n = strlen(s);
for(int l = 0; l < n; l++)
{
for(int i = 0; i < sz; i++)
{
if(val[i] || dp[l][i] == -1)
continue;
for(int j = 0; j < 4; j++)
{
if(val[ch[i][j]])
continue;
int v = ch[i][j];
int sum = 0;
if(idx(s[l]) != j)
sum++;
if(dp[l+1][v] == -1)
dp[l+1][v] = dp[l][i] + sum;
else
dp[l+1][v] = min(dp[l+1][v], dp[l][i] + sum);
if(l + 1 == n)
ans = min(ans, dp[l+1][v]);
}
}
}
printf("Case %d: ", cas++);
if(ans == 999999999)
printf("-1\n");
else
printf("%d\n", ans);
}
return 0;
}