本文介绍两个字符串的编辑距离并给出代码。
编辑距离
所谓编辑距离,就是给定两个字符串后,将一个字符串变为另一个字符串所需要花费的最少步骤。这个改变包括“插入一个字符”、“删除一个字符”,“替换一个字符”。比如:与这两个字符串的编辑距离为4。
编辑距离的求解过程和全局比对是十分相似的(关于全局比对,可以参见前文《序列比对(一)全局比对Needleman-Wunsch算法》),都需要全部符号参与比对,都允许插入、缺失和错配。所以,编辑距离可以用动态规划算法求解,其迭代公式是:
效果如下:
(公众号:生信了)
编辑距离与最长公共子序列
在只允许插入和缺失而不允许错配的情况下,两个字符串的编辑距离可以通过最长公共子序列的长度(关于最长公共子序列,可以参看前文《序列比对(24)最长公共子序列》)间接算出来。
在只允许插入和缺失而不允许错配的情况下,假设给定两个字符串和,长度分别为和。将最长公共子序列的长度记为而将编辑距离记为的话,那么:
求解编辑距离的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXSEQ 1000 #define GAP_CHAR '-' #define min(a, b) ((a) < (b) ? (a) : (b)) #define diff(a, b) ((a) == (b) ? 0 : 1) struct Unit { int W1; // 是否往上回溯一格 int W2; // 是否往左上回溯一格 int W3; // 是否往左回溯一格 int M; // 得分矩阵第(i, j)这个单元的分值,即序列s(1,...,i)与序列r(1,...,j)比对的最低得分 }; typedef struct Unit *pUnit; void strUpper(char *s); void printAlign(pUnit** a, const int i, const int j, char* s, char* r, char* saln, char* raln, int n); void align(char *s, char *r); int main() { char s[MAXSEQ]; char r[MAXSEQ]; printf("The 1st seq: "); scanf("%s", s); printf("The 2nd seq: "); scanf("%s", r); align(s, r); return 0; } void strUpper(char *s) { while (*s != '\0') { if (*s >= 'a' && *s <= 'z') { *s -= 32; } s++; } } void printAlign(pUnit** a, const int i, const int j, char* s, char* r, char* saln, char* raln, int n) { int k; pUnit p = a[i][j]; if (! i && ! j) { // 到达(0, 0) for (k = n - 1; k >= 0; k--) printf("%c", saln[k]); printf("\n"); for (k = n - 1; k >= 0; k--) printf("%c", raln[k]); printf("\n\n"); return; } if (p->W1) { // 向上回溯一格 saln[n] = s[i - 1]; raln[n] = GAP_CHAR; printAlign(a, i - 1, j, s, r, saln, raln, n + 1); } if (p->W2) { // 向左上回溯一格 saln[n] = s[i - 1]; raln[n] = r[j - 1]; printAlign(a, i - 1, j - 1, s, r, saln, raln, n + 1); } if (p->W3) { // 向左回溯一格 saln[n] = GAP_CHAR; raln[n] = r[j - 1]; printAlign(a, i, j - 1, s, r, saln, raln, n + 1); } } void align(char *s, char *r) { int i, j; int m = strlen(s); int n = strlen(r); int m1, m2, m3, minm; pUnit **aUnit; char* salign; char* ralign; // 初始化 if ((aUnit = (pUnit **) malloc(sizeof(pUnit*) * (m + 1))) == NULL) { fputs("Error: Out of space!\n", stderr); exit(1); } for (i = 0; i <= m; i++) { if ((aUnit[i] = (pUnit *) malloc(sizeof(pUnit) * (n + 1))) == NULL) { fputs("Error: Out of space!\n", stderr); exit(1); } for (j = 0; j <= n; j++) { if ((aUnit[i][j] = (pUnit) malloc(sizeof(struct Unit))) == NULL) { fputs("Error: Out of space!\n", stderr); exit(1); } aUnit[i][j]->W1 = 0; aUnit[i][j]->W2 = 0; aUnit[i][j]->W3 = 0; } } for (i = 0; i <= m; i++) { aUnit[i][0]->M = i; aUnit[i][0]->W1 = 1; } for (j = 1; j <= n; j++) { aUnit[0][j]->M = j; aUnit[0][j]->W3 = 1; } // 将字符串都变成大写 strUpper(s); strUpper(r); // 动态规划算法计算得分矩阵每个单元的分值 for (i = 1; i