在程序设计竞赛中,我们时常会遇到序列求最值的问题。在讲今天的问题之前,先小小的说明一下,子序列与子串的问题。
子序列:在原序列中不一定连续;
子串:在原序列中必须连续。
接下来,就开始今天要讲的最长公共子序列LCS(Longest Common
Subsequence).对于LCS这一类的问题,一般是相对于两个序列而言,str[]与ch[]。先假设str的长度为n,ch的长度为m。假设str[]="ASBDAH",ch[]="SDAAH";其中"SDA"是其中的str与ch的一个子序列,但不是最长的。最长的子序列为"SDAH",当然,有时候我们会看到两个串中有多个相同长度的子序列,这不足为奇。
如果我们枚举str的子序列,那么将有2n个子序列,这很不切实际。因此我们可以试着让str中前1,2,3……n参与的情况下分别与ch串中前1,2……m参与情况求出LCS。
dp[i][j]表示str前i个参与,以及ch前j个参与的LCS。
当str[i] == ch[j]时,只需要记录统计出str[1~i - 1],ch[1~j -
1]的LCS,在此基础上加1.
当str[i] != ch[j]时,我们只需要求str[1 ~ i - 1]且ch[1~j],str[1 ~
i]且ch[1~j-1]的最大值即可。
1 #include <iostream>
2 #include <string.h>
3 #include <stdio.h>
4 #include <math.h>
5 using namespace std;
6 const int MAX = 100 + 10;
7 char str[MAX], ch[MAX];
8 int dp[MAX][MAX];
9
10 int max(int a, int b){ return a > b ? a : b;}
11
12 int main()
13 {
14 int t, n, m;
15 scanf("%d", &t);
16 getchar();
17
18 while (t--)
19 {
20 scanf("%s%s", str, ch);
21 n = strlen(str);
22 m = strlen(ch);
23
24 for (int i = 0; i < n; i++)
25 {
26 for (int j = 0; j < m; j++)
27 {
28 if (str[i] == ch[j])
29 {
30 dp[i + 1][j + 1] = dp[i][j] + 1;
31 }
32 else dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]);
33 }
34 }
35
36 printf("%d\n", dp[n][m]);
37
38 }
39
40 return 0;
41 }
如果,仅仅想得到结果,我们还可以将空间压缩一下。由于每次都之后用到i,i-1这两行数据,所以我们可以使用滚动数组。由于动态规划保证无后效性,所以我们完全可以使用一维数组。琢磨一下……
相反如果我们想输出该序列呢,我们也完全可以加一个辅助数组flag[][].当str[i]
== ch[j],flag[i][j] = 0,如果dp[i - 1][j] > dp[i][j - 1],flag[i][j]
= 1,反之,flag[i][j] =
2;以此来记录起路径,最后从flag[n][m]开始往回找。