最长公共子序列LCS


根据题目数据范围可以知道算法的时间复杂度应当是O(nlogn)。具体思路应当是将最长公共子序列问题转到最长上升子序列(LIS问题有时间复杂度O(nlogn))。具体可以参考这篇文章:最长上升子序列
例如:

求上面两个序列的最长公共子序列,可以将上面的的序列 1 6 5 4 3 2 转换成 1 2 3 4 5 6。即有1->1 , 6->2 , 5->3 , 4->4 , 3->5 , 2->6。的对应方法。最后转换成下面这个样子。

求两个序列的公共子序列就是求下面序列的最长上升子序列。下面的代码中translate()函数就是转换的方法。

代码如下:

#include<iostream>//根据题目范围知道这道题要用nlogn 的算法,用n^2的算法会超时,将LCS的问题转化为LIS的问题是解决本题的关键。
#include<algorithm>
using namespace std;
const int maxn = 100005;
int n;
int a[maxn], b[maxn],v[maxn],id[maxn];// id[x] : x被编成了几号
//int dp[maxn];
inline void input()//输入函数
{
	cin >> n;
	for (int i = 1;i <= n;i++)
		cin >> a[i];
	for (int i = 1;i <= n;i++)
		cin >> b[i];
}
inline int query()
{
	v[1] = a[1];
	int ans = 1;
	for (int i = 2;i <= n;i++)
	{
		if (a[i] > v[ans])
			v[++ans] = a[i];
		else//为了使上升子序列最长,应当使序列中的值之间相差尽可能的小。   *p=a[i];的目的就是舍弃之前的值,用新的a[i]来顶替它。
		{
			int* p = lower_bound(v + 1, v + 1 + ans, a[i]);
			*p = a[i];
		}
        
	}
	return ans;
	/*for(int i=1;i<=n;i++)//朴素做法,时间复杂度为n^2
		for (int j = n;j >=1;j--)
		{
			if (a[i] == b[j])
				dp[j] = max(dp[j - 1] + 1, dp[j]);
			else
				dp[j] = max(dp[j], dp[j - 1]);
		}
	return dp[n];*/
}
inline void translate()
{
	for (int i = 1;i <= n;i++)//将数字进行转化的精髓
		id[b[i]] = i;     
	for (int i = 1;i <= n;i++)
		a[i] = id[a[i]];
    /*for (int i = 1;i <= n;i++)
        b[i]=i;*/
}
int main()
{
	ios::sync_with_stdio(false);
	input();
	translate();
    /*for(int i=1;i<=n;i++)
    cout<<a[i]<<" ";
    cout<<endl;
     for(int i=1;i<=n;i++)
    cout<<b[i]<<" ";
    cout<<endl;*/
	int k = query();
	cout << k << endl;
}

以上是关于这道模板题的讲解。下面来对LCS问题进行分析。复杂度为O(n^2)。
对于长度为n的序列A和长度为m的序列B。
具体可以这样思考:
dp[i][j]是长度为i的序列A和长度为j的序列B的最长公共子序列。
1.A[i]==B[j],则有dp[i][j]=dp[i-1][j-1]+1;
2.A[i]!=B[j], 则有dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
int A[105],B[105];
int dp[105][105];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>A[i];
    for(int i=1;i<=m;i++)
    cin>>B[i];
    for(int i=1;i<=n;i++)
       for(int j=1;j<=m;j++)
       {
           if(A[i]==B[j])
               dp[i][j]=dp[i-1][j-1]+1;
           else
               dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
       }
    cout<<dp[n][m]<<endl;
}

参考资料:
https://www.zhihu.com/question/23995189
https://www.luogu.com.cn/blog/pks-LOVING/junior-dynamic-programming-dong-tai-gui-hua-chu-bu-ge-zhong-zi-xu-lie

原文地址:https://www.cnblogs.com/Acapplella/p/13292572.html