序列比对(18)重复匹配问题的补充说明
- 2019 年 10 月 7 日
- 筆記
前文介绍了重复匹配问题的动态规划算法,但是遗留了重复结果输出的问题。本文对该问题进行了补充说明。
前文《序列匹配(五)——重复匹配问题的动态规划算法》介绍了重复匹配问题的动态规划算法。


但是这个公式在回溯时会出现重复结果输出的问题,比如:


校正公式和代码


这样的公式目前还没有出现重复结果输出的问题:



相应的代码放在了文末。
对比对总长度的估计




实现代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define MAXSEQ 1000 #define GAP_CHAR '-' #define UNALIGN_CHAR '.' #define max2(a, b) ((a) > (b) ? (a) : (b)) // 对空位的罚分是线性的 struct FUnit { int W0; // X{i-1}不参与联配 int* Wj; // 跳转到A(i - 1, j) int nj; // Wj数组的大小 float M; // F(i,0)的值 }; typedef struct FUnit* pFUnit; // 00000001 F(i, 0) + s(i, j) // 00000010 是否往左回溯一格 // 00000100 是否往左上回溯一格 // 00001000 是否往上回溯一格 struct AUnit { int Wi; // 不同的bit代表不同的回溯方式 float M; }; typedef struct AUnit* pAUnit; float gap = -2.5; // 对空位的罚分 float match = 5; float unmatch = -4; void strUpper(char *s); float maxArray(float *a, int n); float getFScore(char a, char b); void printFAlign(pFUnit* f, pAUnit** a, const int i, char* s, char* r, char* saln, char* raln, int n, int flag); void printAAlign(pFUnit* f, pAUnit** a, const int i, const int j, char* s, char* r, char* saln, char* raln, int n); void align(char *s, char *r, float t); int main() { char s[MAXSEQ]; char r[MAXSEQ]; float t; printf("The 1st seq: "); scanf("%s", s); printf("The 2nd seq: "); scanf("%s", r); printf("T (threshold): "); scanf("%f", &t); align(s, r, t); return 0; } void strUpper(char *s) { while (*s != '') { if (*s >= 'a' && *s <= 'z') { *s -= 32; } s++; } } float maxArray(float *a, int n) { float max = a[0]; int i; for (i = 1; i < n; i++) { if (a[i] > max) max = a[i]; } return max; } // 替换矩阵:match分值为5,mismatch分值为-4 // 数组下标是两个字符的ascii码减去65之后的和 float FMatrix[] = { 5, 0, -4, 0, 5, 0, -4, 0, -4, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, -4, 0, -4, 0, 0, 0, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5 }; float getFScore(char a, char b) { return FMatrix[a + b - 'A' - 'A']; } void printFAlign(pFUnit* f, pAUnit** a, const int i, char* s, char* r, char* saln, char* raln, int n, int flag) { // flag: 是否是从F(i+1,0)跳转过来,而不是从F(i, 0) + s(i, j)跳转过来 int k; pFUnit p = f[i]; //printf("i=%d, n=%d, flag=%dn", i, n, flag); if (! i) { // 保证序列s的每个字符都比对上 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("nn"); return; } if (flag) { saln[n] = s[i - 1]; raln[n] = UNALIGN_CHAR; } if (p->W0) printFAlign(f, a, i - 1, s, r, saln, raln, n + flag, 1); for (k = 0; k < p->nj; k++) if (p->Wj[k]) printAAlign(f, a, i - 1, k + 1, s, r, saln, raln, n + flag); } void printAAlign(pFUnit* f, pAUnit** a, const int i, const int j, char* s, char* r, char* saln, char* raln, int n) { int k; pAUnit p = a[i][j]; //printf("i=%d, j=%d, n=%dn"); if (! i) { // 保证序列s的每个字符都比对上 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("nn"); return; } if (! j) { // F(i, 0) + d saln[n] = s[i - 1]; raln[n] = GAP_CHAR; printFAlign(f, a, i, s, r, saln, raln, n + 1, 0); } else { if (p->Wi & 1) { // F(i, 0) + s(i, j) saln[n] = s[i - 1]; raln[n] = r[j - 1]; printFAlign(f, a, i, s, r, saln, raln, n + 1, 0); } if (p->Wi & 2) { // 向上回溯一格 saln[n] = s[i - 1]; raln[n] = GAP_CHAR; printAAlign(f, a, i - 1, j, s, r, saln, raln, n + 1); } if (p->Wi & 4) { // 向左上回溯一格 saln[n] = s[i - 1]; raln[n] = r[j - 1]; printAAlign(f, a, i - 1, j - 1, s, r, saln, raln, n + 1); } if (p->Wi & 8) { // 向左回溯一格 saln[n] = GAP_CHAR; raln[n] = r[j - 1]; printAAlign(f, a, i, j - 1, s, r, saln, raln, n + 1); } } } void align(char *s, char *r, float t) { int i, j, k; int m = strlen(s); int n = strlen(r); float tm[4]; float em; // F(m + 1, 0) pFUnit* fUnit; pAUnit** aUnit; char* salign; char* ralign; int alnSize = m + floor(m * abs(match / gap)); // 初始化 if ((fUnit = (pFUnit*) malloc(sizeof(pFUnit) * (m + 1))) == NULL || (aUnit = (pAUnit**) malloc(sizeof(pAUnit*) * (m + 1))) == NULL) { fputs("Error: Out of space!n", stderr); exit(1); } for (i = 0; i <= m; i++) { if ((fUnit[i] = (pFUnit) malloc(sizeof(struct FUnit))) == NULL) { fputs("Error: Out of space!n", stderr); exit(1); } fUnit[i]->W0 = 0; fUnit[i]->nj = n; // 创建F(i, 0)的跳转数组 if ((fUnit[i]->Wj = (int*) malloc(sizeof(int) * fUnit[i]->nj)) == NULL) { fputs("Error: Out of space!n", stderr); exit(1); } for (k = 0; k < fUnit[i]->nj; k++) { fUnit[i]->Wj[k] = 0; } } for (i = 0; i <= m; i++) { if ((aUnit[i] = (pAUnit *) malloc(sizeof(pAUnit) * (n + 1))) == NULL) { fputs("Error: Out of space!n", stderr); exit(1); } for (j = 0; j <= n; j++) { if ((aUnit[i][j] = (pAUnit) malloc(sizeof(struct AUnit))) == NULL) { fputs("Error: Out of space!n", stderr); exit(1); } aUnit[i][j]->Wi = 0; } } fUnit[0]->M = 0; aUnit[0][0]->M = gap; // 注意这里设置A(0,0) != 0 是很有必要的,否则A(0,0)=F(0,0)会导致重复结果输出 for (j = 1; j <= n; j++) aUnit[0][j]->M = gap; // 将字符串都变成大写 strUpper(s); strUpper(r); // 动态规划算法计算得分矩阵每个单元的分值 for (i = 1; i <= m; i++) { // 计算F(i, 0) i >= 1 fUnit[i]->M = fUnit[i - 1]->M; for (j = 1; j <= n; j++) if (fUnit[i]->M < aUnit[i - 1][j]->M - t) fUnit[i]->M = aUnit[i - 1][j]->M - t; if (fUnit[i]->M == fUnit[i - 1]->M) fUnit[i]->W0 = 1; for (j = 1; j <= n; j++) if (fUnit[i]->M == aUnit[i - 1][j]->M - t) fUnit[i]->Wj[j - 1] = 1; // 计算A(i, 0) i >= 1 aUnit[i][0]->M = fUnit[i]->M + gap; aUnit[i][0]->Wi |= 1; // 计算A(i, j) i >= 1 and j >= 1 for (j = 1; j <= n; j++) { tm[0] = fUnit[i]->M + getFScore(s[i - 1], r[j - 1]); tm[1] = aUnit[i - 1][j]->M + gap; tm[2] = aUnit[i - 1][j - 1]->M + getFScore(s[i - 1], r[j - 1]); tm[3] = aUnit[i][j - 1]->M + gap; aUnit[i][j]->M = maxArray(tm, 4); for (k = 0; k < 4; k++) if (tm[k] == aUnit[i][j]->M) aUnit[i][j]->Wi |= 1 << k; } } // 计算F(m + 1, 0) em = fUnit[m]->M; for (j = 1; j <= n; j++) if (em < aUnit[m][j]->M - t) em = aUnit[m][j]->M - t; /* // 打印得分矩阵 for (i = 0; i <= m; i++) printf("%f ", fUnit[i]->M); printf("n"); for (i = 0; i <= m; i++) { for (j = 0; j <= n; j++) printf("%f ", aUnit[i][j]->M); printf("n"); } */ printf("max score: %fn", em); // 打印最优比对结果,如果有多个,全部打印 // 递归法 if (em == 0) { fputs("No seq aligned.n", stdout); } else { if ((salign = (char*) malloc(sizeof(char) * alnSize)) == NULL) { fputs("Error: Out of space!n", stderr); exit(1); } if ((ralign = (char*) malloc(sizeof(char) * alnSize)) == NULL) { fputs("Error: Out of space!n", stderr); exit(1); } if (em == fUnit[m]->M) printFAlign(fUnit, aUnit, m, s, r, salign, ralign, 0, 1); for (j = 1; j <= n; j++) if (em == aUnit[m][j]->M - t) printAAlign(fUnit, aUnit, m, j, s, r, salign, ralign, 0); // 释放内存 free(salign); free(ralign); } for (i = 0; i <= m; i++) { for (j = 0; j <= n; j++) free(aUnit[i][j]); free(aUnit[i]); } free(aUnit); for (i = 0; i <= m; i++) { free(fUnit[i]->Wj); free(fUnit[i]); } free(fUnit); }