类似最长公共子串的一道动态规划题目。
题意
给定两个基因字符串,用A,C,G,T表示其组成成分。若两个基因的长度不一样,可以通过在两个串中分别添加空格使其长度一致。当其长度一样后,分别计算对应位置上的两个字母的分数,并将所有的分数相加便得到两个串的相似度分数。求,两个基因串的最高分数。
分析
给定两个基因串Gn, Gm,要求其最高分数f(n, m),有以下两种情况:
1)G[n] = G[m],则f(n, m) = f(n-1, m-1) + 5;
2)G[n] != G[m], 则可分三种情况:
i.在Gn后添加空格,其分数为f(n, m) = f(n, m-1) + scores[4][Index(G[m])];
ii. 在Gm后添加空格,其分数为f(n, m) = f(n-1, m) + scores[Index(G[n])][4];
iii. 直接使用Gn, Gm的最后两个字配对,其分数为f(n, m) = f(n-1, m-1) + scores[Index(G[n])][Index(G[m])];
其中,Index()返回字母在分数矩阵中的所在行或列。(详见原题分数矩阵)
再取i, ii, iii这三种情况的最大值即为Gn, Gm的最高分数。
代码
#include <iostream> using namespace std; string gen1, gen2; int scores[5][5] = {{5, -1, -2, -1, -3}, {-1, 5, -3, -2, -4}, {-2, -3, 5, -2, -2}, {-1, -2, -2, 5, -1}, {-3, -4, -2, -1, 0}}; int dp[105][105]; //存储结果 int getIndex(char c) { if(c == 'A') return 0; else if(c == 'C') return 1; else if(c == 'G') return 2; else if(c == 'T') return 3; else return 4; } int myMax(int a, int b, int c) { return max(a, max(b, c)); } /*核心函数*/ int f(int len1, int len2) { if(len1 == 0 && len2 == 0) return 0; char char1, char2; int index1, index2; /*传统的LCS不用如此初始化,但本题不同,e.g.,f(0,3) != 0*/ for(int i = 1; i <= len1; i++) { char1 = gen1[i-1]; index1 = getIndex(char1); dp[i][0] = dp[i-1][0] + scores[index1][4]; } for(int i = 1; i <= len2; i++) { char2 = gen2[i-1]; index2 = getIndex(char2); dp[0][i] = dp[0][i-1] + scores[4][index2]; } /*DP过程*/ for(int i = 1; i <= len1; i++) { for(int j = 1; j <= len2; j++) { char1 = gen1[i-1]; char2 = gen2[j-1]; if(char1 == char2) dp[i][j] = dp[i-1][j-1] + 5; else { index1 = getIndex(char1); index2 = getIndex(char2); int sim1, sim2, sim3; sim1 = dp[i-1][j] + scores[index1][4]; sim2 = dp[i][j-1] + scores[4][index2]; sim3 = dp[i-1][j-1] + scores[index1][index2]; dp[i][j] = myMax(sim1, sim2, sim3); } } } return dp[len1][len2]; } int main() { int nCases; cin >> nCases; for(int i = 0; i < nCases; i++) { int len1, len2; cin >> len1 >> gen1; cin >> len2 >> gen2; cout << f(len1, len2) << endl; } return 0; }