最长公共(不连续)子序列(动态规划)
题目
Description
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, ..., xm > another sequence Z = < z1, z2, ..., zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, ..., ik > of indices of X such that for all j = 1,2,...,k, xij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.Input
The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.Output
For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.Sample Input
abcfbc abfcab programming contest abcd mnpSample Output
4 2 0
思想
1. 和其他动态规划题目一样,这类题目无疑还是递归或者递推方法+记忆化方法.
2. 因为这是最长的(可以不连续)的最长子序列,所以仍然可以用当前值如果相同则当前组合的最长值可以用上一个值的最长公共子序列+1;如果不相同,则倒回去,分别位置-1,选其中大的值。
代码
解法1. 递归+记忆化(O(n*m))
/* ------------------------------------------------- Author: wry date: 2022/3/5 9:45 Description: test ------------------------------------------------- */ #include <iostream> #include <algorithm> #include <cmath> #include <cstring> using namespace std; const int MAXN = 1000+10; char str1[MAXN]; char str2[MAXN]; int str[MAXN][MAXN]; //记录以str1的i下标结尾和str2的j下标结尾的公共子序列 int Fun(int i,int j) { int answer; if (i<0 || j<0) { return 0; } if (str[i][j]!=-1) { //递归 return str[i][j]; } else { if (str1[i]==str2[j]) { answer = Fun(i-1,j-1) + 1; //就算是0号位置相同,也能保证Fun(-1,-1)返沪0,+1后为1. } else { answer = max(Fun(i-1,j),Fun(i,j-1)); //如果当前元素不同则返回各自上一个的最大值 } str[i][j] = answer; } return str[i][j]; } int main() { while (cin >> str1 >> str2) { //如果是string类型则无法通过~,只有char才可以 int n = strlen(str1); int m = strlen(str2); memset(str,-1,sizeof(str)); cout << Fun(n-1,m-1) << endl; //以最后一个元素结束,它的最大公共子序列一定是最大的 } return 0; }
解法2. 递推+记忆化(O(n*m))
/* ------------------------------------------------- Author: wry date: 2022/3/5 10:30 Description: test ------------------------------------------------- */ #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 1000+10; char str1[MAXN]; char str2[MAXN]; int str[MAXN][MAXN]; //记录以str1的i下标结尾和str2的j下标结尾的公共子序列 void Fun(int n,int m) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { //每次从上到下处理一行 if (str1[i] == str2[j]) { str[i][j] = str[i-1][j-1] + 1; } else { str[i][j] = max(str[i-1][j], str[i][j-1]); //如果是i=1时,str[0][j]一定都是0,而str[1][j-1]在这个情况下要么是已经处理过的了,要么是str[1][0]也是0,怎么样都是对的 } } } } int main() { while (cin >> str1+1 >> str2+1) { //每个都从下标为1位置开始,0行0列作为哨兵缓冲 int n = strlen(str1+1); int m = strlen(str2+1); //长度也是从1号下标开始计算 memset(str,0,sizeof(str)); Fun(n,m); cout << str[n][m] << endl; //以最后一个元素结束,它的最大公共子序列一定是最大的 } return 0; }