有志者,事竟成,破釜沉舟,百二秦关终属楚。
苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
P1439 【模板】最长公共子序列
题目链接:
https://www.luogu.com.cn/problem/P1439
题目解释:
没什么好解释的,看看时间复杂度, n l o g n nlog_n nlogn求正解,所以简单的DP是不行的,换思路。
问题分析:
这个题应该有很多解,很多博主写过了,主要解法就是用最长上升子序列,至于为什么用最长上升子序列解最长公共子序列,我今天主要解释一下这个问题。
首先看样例:给的数据是:
5
3 2 1 5 4
1 2 3 4 5
首先明确题目中说了,整个序列是1~n的全排列,所以不会出现重复的数字。
题目思路:
1、
先明确一点,两个串的最长公共子序列一定是第一个串的子串,及相对位置一定不变。
2.
先新建一个ind[]数组,用来存储每个数字出现的位置是什么,对于输入的第一个序列,ind[] = {3, 2, 1, 5, 4};这个数组的意思是数字1在第3个出现,数字2在第2个出现,数字3在第1个出现,数字4在第5个出现,数字5在第4个出现。对于ind[]和第一个数组相同,这个是因为测试样例的偶然,并不是因为所有的。
3、
之后在第二个序列中,也就是1 2 3 4 5,用第一个中得到的ind[]位置数组替换第二个数组中的数字,得到3 2 1 5 4。
替换之后的整个数组代表的什么意思,就是第二个序列以第一个序列为基准的的下标值,只要保证这个下标值出现的序列是上升的,就能保证这个转化后的第二个序列在第一个序列中是顺序出现的.
至此整个问题已经转化成了最长上升子序列问题。然后就是最长上升子序列 n l o g n n log_n nlogn的板子。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
int a[N], b[N];
int dp[N] = {0};
int main(){
int n;
ios::sync_with_stdio(false);
cin >> n;
int tmp = 0;
for (int i = 1; i <= n; i++){
cin >> tmp;
a[tmp] = i;
}
for (int i = 1; i <= n; i++){
cin >> tmp;
b[i] = a[tmp];
}
int len = 1;
dp[1] = b[1];
for (int i = 2; i <= n; i++){
if (dp[len] < b[i]) dp[++len] = b[i];
else{
int p = upper_bound(dp + 1, dp + 1 + len, b[i]) - dp;
dp[p] = b[i];
}
}
cout << len << endl;
return 0;
}
板子还是要背下来的,尤其是时间复杂度低的板子。