题目链接
https://www.acwing.com/problem/content/899/
思路
闫氏dp分析法,先上图(感觉放y总照片不太好,就划掉了)后面a表示第一个序列,b表示第二个序列,可能会穿插使用。
状态表示图中已经说得很清楚了,这里状态划分以是否取a序列中的第
i
i
i 个数和b序列中的第
j
j
j 个数作为划分依据,很明显能划分成四种情况,图中椭圆的0,1代表的意思我解释一下。
00:a序列中第
i
i
i 个数不取,b序列中第
j
j
j 个数不取的选法所表示的集合。那么这个状态转移很明显是
f
[
i
−
1
]
[
j
−
1
]
f [ i - 1] [ j - 1 ]
f[i−1][j−1]。
11:a序列中取第
i
i
i 个数,b序列中取第
j
j
j 个数的选法所表示的集合。这种情况很明显只有当
a
[
i
]
=
=
b
[
j
]
a [ i ] == b [ j ]
a[i]==b[j]的时候才会取到,那这个状态转移很明显是
f
[
i
−
1
]
[
j
−
1
]
+
1
f [ i - 1 ] [ j - 1 ]+1
f[i−1][j−1]+1。
01:a序列中不取第
i
i
i 个数,b序列中取第
j
j
j 个数的选法所表示的集合。
10:a序列中取第
i
i
i 个数,b序列中不取第
j
j
j 个数所表示的集合。
后面两种情况我没写状态转移,根据前面00和11的写法,很自然的觉得01的状态转移是
f
[
i
−
1
]
[
j
]
f [ i - 1 ] [ j ]
f[i−1][j],10的状态转移是
f
[
i
]
[
j
−
1
]
f [ i ] [ j - 1 ]
f[i][j−1]。这里就错了。
让我们回顾一下上面状态表示的定义,
f
[
i
]
[
j
]
f [ i ] [ j ]
f[i][j]所表示的是所有在第一个序列的前 i 个字母中出现,且在第二个序列的前 j 个字母中出现的子序列的集合中的长度最大值。那么将
f
[
i
−
1
]
[
j
]
f [ i - 1 ] [ j ]
f[i−1][j]和
f
[
i
]
[
j
−
1
]
f [ i ] [ j - 1]
f[i][j−1]代入这个定义你会发现,这个和我们状态划分的依据并不是等价的,区别在哪?区别在这两个方式表达出来的集合,例如
f
[
i
−
1
]
[
j
]
f [ i - 1 ] [ j ]
f[i−1][j] 它所表示出来的最长公共子序列,对于这里面的的第二个序列来说不一定以
b
[
j
]
b [ j ]
b[j] 结尾,而我们状态划分的依据这里的01是一定取
b
b
b序列中第
j
j
j 个数的,那么它一定以
b
[
j
]
b [ j ]
b[j]结尾,所以这其实并不是等价变形。
但是,我们再进一步分析,虽然不等价,但是我们的目的是要求解最大值而已,对于求解最大值or最小值来说,我的状态有重复其实是不影响的,求数量才会对状态划分有严格的不重叠要求,最大值or最小值无所谓,我们经过思考发现,虽然
f
[
i
−
1
]
[
j
]
f [ i - 1 ] [ j ]
f[i−1][j]和01不等价,但是01所有的情况都包含在
f
[
i
−
1
]
[
j
]
f [ i - 1 ] [ j ]
f[i−1][j]中,我们完全可以在这里用
f
[
i
−
1
]
[
j
]
f [ i - 1 ] [ j ]
f[i−1][j]来求解01对应的这一类,同理
f
[
i
]
[
j
−
1
]
f [ i ] [ j - 1]
f[i][j−1]也一样,而且这样表示之后,00就不需要单独列出来了,因为比如
f
[
i
−
1
]
[
j
]
f [ i - 1 ] [ j ]
f[i−1][j]里面第二个序列不以第 j 个数结尾,那么就相当于都不取了,所以
f
[
i
−
1
]
[
j
]
f [ i - 1 ] [ j ]
f[i−1][j]和
f
[
i
]
[
j
−
1
]
f [ i ] [ j - 1]
f[i][j−1]这两个表示一起把00 01 10三种情况包含完了,所以在代码里你看不到00单独列出来的情况。下面上代码。
代码
#include<bits/stdc++.h>
using namespace std;
char a[1005];
char b[1005];
int f[1005][1005];
int n,m;
int main()
{
scanf("%d%d",&n,&m);
scanf("%s%s",a+1,b+1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(a[i]==b[j])
{
f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
}
}
printf("%d\n",f[n][m]);
return 0;
}