CF1446B
Solution:
根据题目描述我们可以知道一个事情,就是每当字符串 \(a\) 或 \(b\) 只要子串中多一位,那么答案就会少一,如果匹配的子序列多一个字母,那么答案就会多二。以此我们就可以设计出一个 \(dp\) 状态,\(f[i][j]\) 表示以字符串 \(a\) 的第 \(i\) 个位置为结尾和以字符串 \(b\) 的第 \(j\) 个位置为结尾答案最大是多少。可以得到一个转移方程:
\(f[i][j]=max\begin{cases}0\\f[i-1][j]-1\\f[i][j-1]-1\\f[i-1][j-1]+2\;,\;a[i]=b[j]\end{cases}\)
\(0\) 就表示两个子串中最长公共子序列长度为 \(0\) \(f[i-1][j]-1\) 表示从字符串 \(a\) 中增加一位到 \(f[i][j]\) ,\(f[i][j-1]-1\) 与上面同理,\(f[i-1][j-1]+2\;,\;a[i]=b[j]\) 表示当字符串 \(a\) 和 \(b\) 的第 \(i\) 和 第 \(j\) 位相同时,对答案贡献为 \(2\) .
Code:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-') f=0;c=getchar();}
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return f?x:-x;
}
const int N=5010;
int n,m,f[N][N],ans;
char a[N],b[N];
int main()
{
n=read(),m=read();
scanf("%s\n%s",a+1,b+1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
f[i][j]=max({0,f[i-1][j]-1,f[i][j-1]-1});
if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+2);
ans=max(ans,f[i][j]);
}
}
printf("%d",ans);
return 0;
}