最长公共子序列 SSL1463 (动态规划)

题目描述:

在这里插入图片描述


解题思路:

这题可以用暴搜枚举每一种可能,找出最优解,然后愉快超时。
分析题目,长度为 n n n 的两个序列的最长公共子序列长度必然源于长度 n − 1 n-1 n1 的两个序列的最长公共子序列长度,因此可以判断该问题具有最优子结构性,考虑动态规划。

这道题需要用到一种非常经典的线性DP,公共子序列DP。
俗话说得好,几乎每一种模板类型的DP的推出都离不开建模(就是画图!),而分析DP的办法最好就是打表,因此,在我们完全不知道DP方程的情况下,我们可以出一个小一点的数据,尝试打个表,自己推出DP

设这题数据为:
序列X为      2      4      6      7 \ \ \ \ 2\ \ \ \ 4\ \ \ \ 6\ \ \ \ 7     2    4    6    7
序列Y为      2      3      4      7 \ \ \ \ 2\ \ \ \ 3\ \ \ \ 4\ \ \ \ 7     2    3    4    7
把不同长度情况下序列的最长公共子序列的长度列出来:
在这里插入图片描述
先不说啥,设定状态,我们用 d p i , j dp_{i,j} dpi,j 表示表格中第 i i i 行第 j j j 列的数,其中, d p i , j dp_{i,j} dpi,j 表示从 X 序列的第 i i i 个到 Y 序列的第 j j j 个之间的最长公共子序列的长度。

我们用DP的思维方式来模拟填表过程。
首先看到 X 1 X_1 X1 Y 1 Y_1 Y1,相等。很明显,由于 X 1 X_1 X1 Y 1 Y_1 Y1 的相等, d p 1 , 1 dp_{1,1} dp1,1 很明显就等于1,意味着在 X 1 − 1 X_{1-1} X11 Y 1 − 1 Y_{1-1} Y11 这个区间内,由于元素的相等,使得公共子序列长度增加了1。

但是这样这太肤浅了,我们需要明白的是——这里是怎么变成 1 的。

还记得,我们上文说该问题具有最优子结构性。没错,这就是问题解决的关键。众所周知,任何具有最优子结构性问题的最优解,都是在子问题的最优解之上得来的。,明白了这个定义,我们就能明白图中的 d p 1 , 1 dp_{1,1} dp1,1 是怎么得出来的。

最长公共子序列具有最优子结构性质,设序列 X = X 1 , X 2 , … … X m X={ X_1,X_2, ……X_m } X=X1,X2,Xm Y = Y 1 , Y 2 … … , Y n Y={Y_1,Y_2……,Y_n} Y=Y1,Y2Yn 的最长公共子序列为 Z = Z 1 , Z 2 … … Z k Z={Z_1,Z_2……Z_k} Z=Z1,Z2Zk

X m = Y n X_m=Y_n Xm=Yn,则 Z k = X m = Y n Z_k=X_m=Y_n Zk=Xm=Yn,且 Z k − 1 Z_k-1 Zk1 X m − 1 X_m-1 Xm1 Y n − 1 Y_n-1 Yn1 的最长公共子序列,因此 d p m , n = d p m − 1 , n − 1 + 1 dp_{m,n}=dp_{m-1,n-1}+1 dpm,n=dpm1,n1+1
X m   ! = Y n X_m\ !=Y_n Xm !=Yn,则 d p m , n = m a x ( d p m − 1 , n   ,   d p m , n − 1 ) dp_{m,n}=max(dp_{m-1,n}\ ,\ dp_{m,n-1}) dpm,n=max(dpm1,n , dpm,n1)
再简单一点,按表格的形式来说,就是如果 X m = Y n X_m=Y_n Xm=Yn,那么 d p m , n dp_{m,n} dpm,n 等于表格中左上角的数 +1,否则等于表格左边和表格上面中稍大的数。

固有状态转移方程:

X m = Y n X_m=Y_n Xm=Yn d p m , n = d p m − 1 , n − 1 + 1 dp_{m,n}=dp_{m-1,n-1}+1 dpm,n=dpm1,n1+1

否则 d p m , n = m a x ( d p m − 1 , n   ,   d p m , n − 1 ) dp_{m,n}=max(dp_{m-1,n}\ ,\ dp_{m,n-1}) dpm,n=max(dpm1,n , dpm,n1)


CODE:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
string str1,str2;
char a[1001],b[1001];
int dp[1001][1001]={
    
    0},ans=0;
void input()
{
    
    
	cin>>str1>>str2;
	for(int i=0;i<str1.size();i++)
	  a[i+1]=str1[i];
	for(int i=0;i<str2.size();i++)
	  b[i+1]=str2[i];
}

void DP()
{
    
    
	for(int i=1;i<=str1.size();i++)
	  {
    
    
	  	for(int j=1;j<=str2.size();j++)
	  	  {
    
    
	  	  	if(a[i]==b[j])
	  	  	  dp[i][j]=dp[i-1][j-1]+1;
	  	  	else
	  	  	  {
    
    
	  	  	    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);	
			  }
			ans=max(ans,dp[i][j]);
		  }
	  }
	cout<<ans;
}

int main()
{
    
    
	input();
	DP();
	return 0;
} 

总结:

DP的方程分析应该从子问题考虑。

能在鄙人这里得到一丁点启发的朋友们,欢迎留下你们的支持!!:)

如果你喜欢我的内容,那么也请支持一下他吧

猜你喜欢

转载自blog.csdn.net/SAI_2021/article/details/119952434