[模板] LIS(最长上升子序列)和 LCS(最长公共子序列)

LCS与LIS的同质性

此段无比伟大而清晰的解释来自阮行止。我仅调整了B序列的顺序,使之最初不为顺序序列(因为我的代码实现在这里栽了跟斗orz)。

关于(LCS)为什么可以转化成LIS问题,这里提供一个解释。
比如这样的两个序列:

A:3 2 1 4 5
B:2 5 4 1 3

我们不妨给它重新编个号,使得A的逆序为0,即3-a(3标成a),2-b,1-c,4-d,5-e。于是成为:

A: a b c d e
B: b e d c a

这样标号后,LCS长度显然不会因此改变,毕竟没有对原有序列进行任何相对位置的改变。
于是有如下三段论推理

  1. A本身是单调递增的,A的任意子序列单调。
  2. 两个序列的公共子序列,一定是A的子序列。
  3. 因此这个子序列也是单调递增的。

换句话说:我们将共用关系,转化成了重新编号规则下的单调递增关系!

那么现在,为了在B中寻找与A的共用片段,只需要在B中任取(因为2个序列相同数组的排列)递增的子序列即可。
最长的递增子序列是什么问题呢?

你猜对了小朋友,是LIS(doge

LCS模板

#include <bits/stdc++.h>
using namespace std;
const int N = 100001;
int n, m[N], mp[N], k[N], r[N], p = 1, ans;
int main()
{
    cin >> n;
//通过映射,将LCS转化为LIS问题
    for (int i = 1; i <= n; i++)
        cin >> m[i], mp[m[i]] = i;
    for (int i = 1; i <= n; i++)
        cin >> k[i], k[i] = mp[k[i]];

//nlogn的LIS算法
    r[0] = k[1];//事先考虑初始特例,使得分支简化
    for (int i = 2; i <= n; i++)
    {
        auto it = upper_bound(r, r+p, k[i]);
        if (it == r+p) r[p++] = k[i];
        else *it = k[i];
    }
    cout << p;
}

LIS模板

关于LIS,这里是一个简略写法。如果原序列不限制相同,会出现危险的全同情况。于是我们可以有:

#include <bits/stdc++.h>
using namespace std;
int r[1005], p, tmp;
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> tmp;
		auto it = upper_bound(r, r + p, tmp);
		if (it == r + p)//搜到末端
			if (*(it-1) < tmp || p == 0)//如果不全同,或者为初态特例
				dp[ptr++] = tmp;
			else continue;//这个表示全同。不能加到上升序列当中。
		else *it = tmp;
	}
	cout << ptr << endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_45502929/article/details/106750817