最长上升子序列(LIS)和最长公共子序列(LCS)

最长上升子序列

DP(O(n^2^))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>

using namespace std;

const int N = 5010;

int f[N], a[N], n; //f[i]:以a[i]结尾的最长上升子序列

int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
f[i] = 1;
}

for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= i - 1; j ++ )
if (a[j] < a[i])
f[i] = max(f[i], f[j] + 1);

int res = 0;
for (int i = 1; i <= n; i ++ ) res = max(res, f[i]);
cout << res;

return 0;
}

贪心+二分(O(nlogn))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>

using namespace std;

const int N = 200010;

int q[N], a[N], n; //q[i]:长度为i的子序列最小的结尾值

int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

int len = 0;
q[0] = -1e9;
for (int i = 1; i <= n; i ++ )
{
//二分找出q中<a[i]的第一个数
int l = 0, r = len;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (q[mid] < a[i]) l = mid;
else r = mid - 1;
}
q[r + 1] = a[i];
len = max(len, r + 1);
}
cout << len;

return 0;
}

最长公共子序列

DP(O(n^2^))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

using namespace std;

const int N = 10010;

int f[N][N]; //f[i][j]:a前i个字母, b前j个字母的最长公共子序列的长度
int a[N], b[N];
int n;

int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);


//划分 a[i]/b[j]是否在最长公共子序列中 共四种情况
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
{
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
if (a[i] == b[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
}

cout << f[n][n];

return 0;
}

贪心+二分(O(nlogn))

思路:转换为最长上升子序列问题
将A序列看作单调递增,把b的每个元素用a的索引替换如:
A: a b c d e
B: c b a d e
因此只要求B相对于A的最长上升子序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>

using namespace std;

const int N = 200010;

//求a的最长上升子序列
int q[N], a[N], n; //q[i]:长度为i的子序列最小的结尾值
int x[N], y[N], mp[N];

int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &x[i]);
mp[x[i]] = i; //用于转换y序列
}
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &y[i]);
a[i] = mp[y[i]];
}


for (int i = 1; i <= n; i ++ ) cin >> a[i];

int len = 0;
q[0] = -1e9;
for (int i = 1; i <= n; i ++ )
{
//二分找出q中<a[i]的第一个数
int l = 0, r = len;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (q[mid] < a[i]) l = mid;
else r = mid - 1;
}
q[r + 1] = a[i];
len = max(len, r + 1);
}
cout << len;

return 0;
}