问题描述
Please implement solution for Longest Common Subsequence problem with any programming language and analyze its time complexity.
翻译
请使用任何编程语言实现最长公共子序问题的解决方案,并分析其时间复杂性。
最长公共子序列:找两个数组中的最大子集
思路
- 初始化
a[i][0] = 0
和a[0][j] = 0
,表示当其中一个字符串为空时,最长公共子序列的长度为 0。 - 从
i=1
和j=1
开始遍历两个字符串,如果x[i] == y[j]
,则a[i][j] = a[i-1][j-1] + 1
,表示当前字符属于最长公共子序列,且长度加一;否则,取a[i-1][j]
和a[i][j-1]
中的较大值。 - 在填表的同时,使用另一个二维数组
flag
记录每个子问题的解的来源,方便后续的回溯。 - 最后,从
flag
数组中根据填表的结果进行回溯,找出最长公共子序列。
代码:第一二种利于理解回溯
1.严格填表
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <algorithm>
#include <stdlib.h>
#include <vector>
using namespace std;
//The longest commom subsequence
const int N=1e3+10;
int LCSlength(char*x,char *y ,vector<vector<int> >& a, vector<vector<int> >& flag, int n, int m)
{
for (int i = 0; i <m; i++)//在开始时,只有X或只有Y无公共序列
a[i][0] = 0;
for (int i = 0; i <n; i++)
a[0][i] = 0;
for (int i = 1; i <=n; i++)
{
for (int j = 1; j <=m; j++)
{
if (x[i] == y[j])//相同,加入子序列
{
a[i][j] = a[i - 1][j - 1] + 1;
flag[i][j] = 1;//flag=1表示相同
}
else if (a[i - 1][j] >=a[i][j - 1])
{
a[i][j] = a[i - 1][j];
flag[i][j] = 2;//flag=2表示Xi-1和Yj得来
}
else
{
a[i][j] = a[i][j - 1];
flag[i][j] = 3;//flag=3表示由Xi和Yj-1得来
}
}
}
return a[n][m];
}
void LCS(char *a, vector<vector<int> >& flag, int i, int j)//x和y任意一个可以做参数传过来,i,j表示当前递归到的位置
{
if (i == 0 || j == 0)
return;
if (flag[i][j] == 1)
{
LCS(a, flag, i - 1, j - 1);//试想i,j相同,不应该i-1 与j 或j-1 与i比较(图)
cout << a[i]<<" ";//最后一个相同子序列,所以应该先递归在输出 //为什么不输出a[j]?
}
else if (flag[i][j] == 2)
{
LCS(a, flag, i - 1, j);//从a[i-1][j]得来,说明有[i-1][j]
//为什么此处不输出?//由他得来并不是公共子序列
}
else
{
LCS(a, flag, i , j-1);//同上
}
}
int main()
{
char x[N], y[N];
int n=0, m = 0;
while (1)
{
char ch = getchar();
if (ch == '\n')
break;
x[n++] = ch;
}
while (1)
{
char ch = getchar();
if (ch == '\n')
break;
y[m++] = ch;
}
vector<vector<int> >a(n+1,vector<int>(m+1,0));//a[i][j]代表遍历到第Xi与Yj的最长公共子序列的长度
vector<vector<int>>flag(n+1, vector<int>(m+1,0));//代表第a[i][j]个的由来,方便回溯
int length = LCSlength(x, y, a, flag, n, m);
LCS(x, flag, n-1, m-1);
return 0;
}
2.中规中矩
//时间复杂度是O(a*b),最长公共子序列问题
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
string Lcs(string X, string Y)
{
int a = X.length();
int b = Y.length();
vector<vector<int>> dp(a + 1, vector<int>(b + 1));
for (int i = 1; i <= a; i++)
{
for (int j = 1; j <= b; j++)
{
if (X[i - 1] == Y[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
string lcs;
int i = a;
int j = b;
while (i > 0 && j > 0) {
if (X[i - 1] == Y[j - 1]) {
lcs.push_back(X[i - 1]);
--i;
--j;
}
else if (dp[i - 1][j] > dp[i][j - 1]) {//由前一个状态得来
--i;
}
else {
--j;
}
}
reverse(lcs.begin(), lcs.end());
return lcs;
}
int main()
{
string X = "ABCBDAB";
string Y = "BDCABA";
cout << "The LCS of " << X << " and " << Y << " is " << Lcs(X, Y);
return 0;
}
3.投机该死
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s1, s2;
cin >> s1 >> s2;
int n = s1.length(), m = s2.length();
s1 = " " + s1, s2 = " " + s2;
vector<vector<int>>dp(n+1,vector<int>(m+1));
vector<char>res;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (s1[i] == s2[j]) { res.push_back(s1[i]); dp[i][j] = dp[i - 1][j - 1] + 1; }
else { dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); }
}
}
for (auto x : res) { cout << x; }
cout <<endl<< dp[n][m];
return 0;
}
try
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <algorithm>
#include <stdlib.h>
#include <vector>
using namespace std;
string LSC(string a,string b)
{
string s;
vector<vector<int>>dp(a.size() + 1, vector<int>(b.size() + 1,0));//多一行多一列,初始化为0
for(int i=1;i<=a.size();i++)
{
for (int j = 1; j <= b.size() ; j++)
{
if (a[i - 1] == b[j - 1])//第一种情况 相等则+1
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
dp[i][j] = max(dp[i - 1][j], dp[i][j-1]);//一个前面-1 一个后面-1,对称【cy】
}
}
int i = a.size(), j = b.size();
while (i > 0 && j > 0)
{
if (a[i - 1] == b[j - 1])
{
s.push_back(a[i - 1]);//【cy】 pushback
i--; j--;//【cy】这里也要减
}
else if (dp[i - 1][j] > dp[i][j - 1])//【cy对称】
i--;
else
j--;
}
reverse(s.begin(), s.end());//【cy】
return s;
}
int main()
{
string a, b;
cin >> a >> b;
string ans = LSC(a, b);
cout << ans << endl;
return 0;
}
测试案例
BDCABA
ABCBDAB
运行结果
BCBA
时间复杂度分析
O(m*n)
速记
vector<vector<int> >a(n+1,vector<int>(m+1,0));//a[i][j]代表遍历到第Xi与Yj的最长公共子序列的长度
vector<vector<int>>flag(n+1, vector<int>(m+1,0));//代表第a[i][j]个的由来,方便回溯
其中n+1、m+1是表格中多出一行一列0
注意看几个【cy】之处,当a[i-1]!=b[j-1]时候,应该在dp[i-1][j]、dp[j-1][i]里面取最大值
回溯时从后往前回溯,不管怎么样都有东西要--,哪个大哪个--。
要reverse
两个for是<=size(),不是size+1