题目
给定两个长度分别为 和 的字符串 和 ,求既是 的子序列又是 的子序列的字符串长度最长是多少。
输入格式
第一行包含两个整数 和 。
第二行包含一个长度为 的字符串,表示字符串 。
第三行包含一个长度为 的字符串,表示字符串 。
字符串均由小写字母构成。
输出格式
输出一个整数,表示最大长度。
数据范围
输入样例:
4 5
acbd
abedc
输出样例:
3
解题
方法一:动态规划
思路
注意:以下分析均基于两字符串下标从 开始。
思维过程:
如何去求集合划分出的四类(以下以 指代以上四类):
- :显然地,根据状态表示,这一类的最大值为: 。
- :类似地,这一类的最大值表示为: ,吗?
并不是这样,确切来说,该类表示的集合是 表示的集合的子集,因为:根据状态定义, 表示所有从 与 中选出的公共子序列,注意:此时 并不是被必选的,而 这一类的集合要求是必定包含 ,所以上面说它是子集。
但是,用 来只带这一类的最大值是可行的,因为我们只需要保证集合划分得不重不漏,而求最大值时是可以重复的。
- :该类表示的集合是 表示的集合的子集,理由同上。
- :由于 是 中最后一个字符, 也是 中最后一个字符,所以若想让公共子序列中同时包含 和 就必须让 ,此时这一类的最大值恰好是 。
综上:
动态规划:
- 状态定义: 表示字符串 中前 个字符与字符串 中前 个字符构成的公共子序列的最长长度。
- 状态转移方程: 。
- 初始状态:进行第一次状态转移前,所有位置的公共子序列的最大长度均为 。
代码
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
in.nextToken();
int n = (int) in.nval;
in.nextToken();
int m = (int) in.nval;
char[] a = ('\0' + br.readLine()).toCharArray();
char[] b = ('\0' + br.readLine()).toCharArray();
int[][] dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
if (a[i] == b[j]) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
}
System.out.println(dp[n][m]);
}
}
#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
char a[N], b[N];
int dp[N][N];
int main() {
scanf("%d%d\n%s\n%s", &n, &m, a + 1, b + 1);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
if (a[i] == b[j]) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
printf("%d\n", dp[n][m]);
return 0;
}
评论区