题目描述:
解题思路:
这题可以用暴搜枚举每一种可能,找出最优解,然后愉快超时。
分析题目,长度为 n n n 的两个序列的最长公共子序列长度必然源于长度 n − 1 n-1 n−1 的两个序列的最长公共子序列长度,因此可以判断该问题具有最优子结构性,考虑动态规划。
这道题需要用到一种非常经典的线性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} X1−1 和 Y 1 − 1 Y_{1-1} Y1−1 这个区间内,由于元素的相等,使得公共子序列长度增加了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,Y2……,Yn 的最长公共子序列为 Z = Z 1 , Z 2 … … Z k Z={Z_1,Z_2……Z_k} Z=Z1,Z2……Zk
若 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 Zk−1 是 X m − 1 X_m-1 Xm−1 和 Y n − 1 Y_n-1 Yn−1 的最长公共子序列,因此 d p m , n = d p m − 1 , n − 1 + 1 dp_{m,n}=dp_{m-1,n-1}+1 dpm,n=dpm−1,n−1+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(dpm−1,n , dpm,n−1)。
再简单一点,按表格的形式来说,就是如果 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=dpm−1,n−1+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(dpm−1,n , dpm,n−1)
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的方程分析应该从子问题考虑。
能在鄙人这里得到一丁点启发的朋友们,欢迎留下你们的支持!!:)
如果你喜欢我的内容,那么也请支持一下他吧