我们经常会听说 DNA 亲子鉴定是怎么回事呢?人类的 DNA 由 4 个基本字母{A,C,G,T}构成,包含了成千上亿个字符。如果两个人的 DNA序列相差 0.1%,仍然意味着有 300 万个位置不同,所以我们通常看到的 DNA 亲子鉴定报告上结论有:相似度 99.99%,不排除亲子关系。
怎么判断两个基因的相似度呢?生物学上给出了一种编辑距离的概念。
例如两个字符串 FAMILY 和 FRAME,有两种
对齐方式:
F - A M I L Y - F A M I L Y
F R A M E F R A M E
第 1 种对齐需要付出的代价:4,插入 R,将 I 替换为 E,删除 L、Y。
第 2 种对齐需要付出的代价:5,插入 F,将 F 替换为 R,将 I 替换为 E,删除 L、Y。
编辑距离是指将一个字符串变换为另一个字符串所需要的最小编辑操作。
怎么找到两个字符串 x[1,…,m]和 y[1,…,n]的编辑距离呢?
分析问题:
如果直接暴力枚举,可想而知需要运算的次数会随着字符串的增长而暴增,我们可以通过分析它是否具有最优子结构来决定能不能用动态规划。
假设的d[i][j]是X和Y的编辑距离最优解。那么不管两序列怎么对齐,都只可能有以下三种:
①:需要删除xi,付出代价+1,即d[i][j]=d[i-1][j]+1;
②:需要删除yj,付出代价+1,即d[i][j]=d[i][j-1]+1;
③:如果xi≠yj,则需要替换,付出代价+1,即d[i][j]=d[i-1][j-1]+1;若xi=yj,则付出代价为0;
我们可以由上推出最优值递归式:d[i][j]=min{d[i-1][j]+1,d[i][j-1]+1,d[i-1][j-1]+flag}//flag用xi是否等于yj来赋值。
代码详解:
#include<iostream>
#include<string.h>
using namespace std;
const int N = 1000;
char s1[N], s2[N];
int d[N][N];
int min(int a,int b)
{
return a < b ? a : b;
}
int judge(char *s1, char *s2)
{
int len1 = strlen(s1);
int len2 = strlen(s2);
for (int i = 0; i <= len1; i++)
d[i][0] = i;
for (int i = 0; i <= len2; i++)
d[0][i] = i;
for (int i = 1; i <= len1; i++)
{
for (int j = 1; j <= len2; j++)
{
int flag;//用来标志xi和yj是否相等
if (s1[i] == s2[j])
flag = 0;
else
flag = 1;
int temp = min(d[i - 1][j] + 1, d[i][j - 1] + 1);//先比较前两个的值
d[i][j] = min(temp, d[i - 1][j - 1] + flag);
}
}
return d[len1][len2];
}
int main()
{
cin >> s1;
cin >> s2;
cout << judge(s1, s2) << endl;
return 0;
}