本题的思路和最长公共子序列有相似之处,递推方程很相似
当我们,遍历到上面的i,下面的j时,设dp[i][j]为上面前i个,下面前j个匹配时的相似度。
由于i,j不一定匹配,故分为三种情况
i,j匹配 dp[i][j]=dp[i-1][[j-1] (前i-1,j-1匹配的结果)+ f(s[i],t[j]) (相似度)
i和空格匹配,j和i-1匹配,dp[i][j]=dp[i-1][j]+ f(s[i],---)(相似度)
j和空格匹配,i和j-1匹配 dp[i][j]=dp[i][j-1]+ f(---,t[j]) (相似度)
刚开始我担心的是,一旦某个位置和空格进行了匹配,那么后面的匹配势必会受到向后推移一位的影响。
而我们对于当前i,j我们这三项操作保证了前i前j个非空碱基,注意是非空都得到了匹配。当遍历到后面时,处理的也是前i1,j1的情况,而前i1-1,j1 前 i1,j1-1 ,前i1-1,j1-1已经得到了匹配,并不会受到位置推移影响。也就是说,例如,即使前i-1,j已经匹配了好多空格,但它们还是两两完成了匹配,不会把i,j推后。
和这题的递推差不多P1435 [IOI2000] 回文字串 --LCS最长公共序列_jisuanji2606414的博客-CSDN博客
#include<iostream>
#include<set>
# include<algorithm>
# include<cstring>
using namespace std;
typedef long long int ll;
int a[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,-999999999}
};
int dp[110][110];
int ss[110],tt[110];
int change(char ch)
{
if(ch=='A')
return 0;
if(ch=='C')
return 1;
if(ch=='G')
return 2;
if(ch=='T')
return 3;
}
int main()
{
fill(dp[0],dp[0]+110*110,-999999);
int n,m;
cin>>n;
string s;
cin>>s;
for(int i=0; i<s.length(); i++)
{
ss[i+1]=change(s[i]);
}
cin>>m;
cin>>s;
for(int i=0; i<s.length(); i++)
{
tt[i+1]=change(s[i]);
}
dp[0][0]=0;
for(int i=1;i<=n;i++)
{
dp[i][0]=dp[0][i-1]+a[ss[i]][4];
}
for(int i=1;i<=m;i++)
{
dp[0][i]=dp[0][i-1]+a[tt[i]][4];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dp[i][j]=max(dp[i-1][j-1]+a[ss[i]][tt[j]],dp[i-1][j]+a[ss[i]][4]);
dp[i][j]=max(dp[i][j],dp[i][j-1]+a[tt[j]][4]);
}
}
cout<<dp[n][m];
return 0;
}