题意,有两个字符串,分别由四个字母构成,字母之间有不同的相似度,允许在两个字符串都按原顺序排列的情况下进行字母与字母之间的匹配,也可以让字母与空格匹配,即相当于在字符串中间加空格来一一匹配,每个字母与空格也有相应的相似度,但空格不能和空格匹配。问当给出两个字符串时,求它们的最大相似度。
我一开始的想法也是想类似于最长公共子序列的做法,只是将匹配数变成了一个权值,总的相似度。这个方向是正确的,但是我却在细节上犯了糊涂,我一直在考虑当第一条链的第 i 个和第二条的第 j 个相匹配,这样的情况,从前面的各种情况转移到这里。但事实上,其实原本就该想到,最长公共子序列的状态转移也是对于匹配完第一条的第 i 个和第二条的第 j 个,而不是这两个恰好匹配。这样做反而容易进行状态转移。
状态转移关系如下:
dp [ i ] [ j ] 表示匹配完第一条的第 i 个和第二条的第 j 个(均可以与空格进行匹配);
① dp [ i - 1 ] [ j ] + v (第一条第 i 个与空格匹配后增加的相似度) ,当该值大于 dp 原值时就用它优化 dp;
② dp [ i ] [ j - 1 ] + v (第二条第 j 个与空格匹配后增加的相似度) ,当该值大于 dp 原值时就用它优化 dp;
③ dp [ i - 1 ] [ j - 1 ] + v(第一条第 i 个与第二条第 j 个匹配后增加的相似度) ,当该值大于 dp 原值时就用它优化 dp;
这样就行了;
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #define max(a,b) a>b?a:b 5 #define INF 100000 6 using namespace std; 7 int v[5][5]={{0,-3,-4,-2,-1},{-3,5,-1,-2,-1},{-4,-1,5,-3,-2},{-2,-2,-3,5,-2},{-1,-1,-2,-2,5}}; 8 int dp[105][105]; 9 int s1[105],s2[1059]; 10 11 int main(){ 12 int n; 13 while(scanf("%d",&n)!=EOF){ 14 for(int q=1;q<=n;q++){ 15 memset(s1,0,sizeof(s1)); 16 memset(s2,0,sizeof(s2)); 17 int l1,l2; 18 scanf("%d",&l1); 19 int i,j; 20 getchar(); 21 char a; 22 dp[0][0]=0; 23 for(i=1;i<=l1;i++){ 24 a=getchar(); 25 if(a=='A') s1[i]=1; 26 else if(a=='C') s1[i]=2; 27 else if(a=='G') s1[i]=3; 28 else if(a=='T') s1[i]=4; 29 dp[i][0]=dp[i-1][0]+v[s1[i]][0]; 30 } 31 scanf("%d",&l2); 32 getchar(); 33 for(i=1;i<=l2;i++){ 34 a=getchar(); 35 if(a=='A') s2[i]=1; 36 else if(a=='C') s2[i]=2; 37 else if(a=='G') s2[i]=3; 38 else if(a=='T') s2[i]=4; 39 dp[0][i]=dp[0][i-1]+v[0][s2[i]]; 40 } 41 int k; 42 /* 43 printf("s1:"); 44 for(i=1;i<=l1;i++)printf("%d",s1[i]); 45 printf(" "); 46 printf("s2:"); 47 for(i=1;i<=l2;i++)printf("%d",s2[i]); 48 printf(" "); 49 */ 50 for(i=1;i<=l1;i++){ 51 for(j=1;j<=l2;j++){ 52 dp[i][j]=-INF; 53 dp[i][j]=max(dp[i][j],dp[i-1][j]+v[s1[i]][0]); 54 dp[i][j]=max(dp[i][j],dp[i][j-1]+v[0][s2[j]]); 55 dp[i][j]=max(dp[i][j],dp[i-1][j-1]+v[s1[i]][s2[j]]); 56 } 57 } 58 printf("%d ",dp[l1][l2]); 59 } 60 } 61 return 0; 62 }