最长公共子序列(Longest Common Subsequence)
1. 问题描述
给定序列 X 和 Y,若 S 同时为 X 和 Y 的子序列,则称 S 为 X 和 Y 的公共子序列;若 S 还是 X 和 Y 的所有公共子序列中最长的,则称 S 是 X 和 Y 的一个最长公共子序列。
注意区别最长公共子串(要求连续)和最长公共子序列(不要求连续)。
最长公共子序列不唯一。
2. 动态规划解
用 opt[i, j] 表示 X[1 : i] 和 Y[1 : j] 的最长公共子序列的长度。则显然有边界条件:
- opt[0, j] = 0
- opt[i, 0] = 0
另外还有递归方程:
- 如果 Xi == Yj,则 opt[i, j] == 1 + opt[i - 1, j - 1]
- 如果 Xi!= Yj,则 opt[i, j] = max(opt[i - 1, j], opt[i, j - 1])
3. c++实现
- 只需要知道最长公共子序列的长度,不需要知道最长公共子序列是什么:
template<class Iter1, class Iter2> int LCS(const Iter1 &beg1, const Iter1 &end1, const Iter2 &beg2, const Iter2 &end2) { int m = end1 - beg1; int n = end2 - beg2; std::vector<int> opt1(n + 1, 0); std::vector<int> opt2(n + 1, 0); Iter1 id1 = beg1; Iter2 id2; for(int i = 1; i <= m; ++i, ++id1) { id2 = beg2; for(int j = 1; j <= n; ++j, ++id2) opt2[j] = (*id1 == *id2) ? (opt1[j - 1] + 1) : std::max(opt1[j], opt2[j - 1]); if(i < m) opt1.swap(opt2); } return opt2[n]; }
- 需要知道最长公共子序列是什么:输出最长公共子序列在第一个输入序列中的下标(从 0 开始)
template<class Iter1, class Iter2> std::vector<int> LCS2(const Iter1 &beg1, const Iter1 &end1, const Iter2 &beg2, const Iter2 &end2) { typedef std::vector<std::vector<int>> Matrix; int m = end1 - beg1; int n = end2 - beg2; Matrix opt(m + 1, std::vector<int>(n + 1)); Matrix state(m + 1, std::vector<int>(n + 1)); for(int i = 0; i <= m; ++i) opt[i][0] = 0; for(int j = 0; j <= n; ++j) opt[0][j] = 0; Iter1 id1 = beg1; Iter2 id2; for(int i = 1; i <= m; ++i, ++id1) { id2 = beg2; for(int j = 1; j <= n; ++j, ++id2) { if(*id1 == * id2) { state[i][j] = 0; opt[i][j] = opt[i - 1][j - 1] + 1; } else if(opt[i - 1][j] > opt[i][j - 1]) { state[i][j] = 1; opt[i][j] = opt[i - 1][j]; } else { state[i][j] = 2; opt[i][j] = opt[i][j - 1]; } } } std::vector<int> ans(opt[m][n]); int i = m, j = n, id = opt[m][n] - 1; while(id >= 0) { if(state[i][j] == 0) { ans[id--] = i - 1; --i; --j; } else if(state[i][j] == 1) --i; else --j; } return ans; }
4. 分析
时间复杂度 O(m*n)
空间复杂度分别为 O(n) 和 O(m * n)
老实为人,踏实为学