前文介绍了重复匹配问题的动态规划算法,但是遗留了重复结果输出的问题。本文对该问题进行了补充说明。
前文《序列匹配(五)——重复匹配问题的动态规划算法》介绍了重复匹配问题的动态规划算法。
但是这个公式在回溯时会出现重复结果输出的问题,比如:
这样的公式目前还没有出现重复结果输出的问题:
相应的代码放在了文末。
#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 != '\0') {
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=%d\n", 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("\n\n");
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=%d\n");
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("\n\n");
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: %f\n", 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);
}