题目地址
时间复杂度 O ( n 2 ) O(n^{2}) O(n2)方法
思路
我们先举个栗子,比如两个序列,分别命名为
n
u
m
1
num1
num1和
n
u
m
2
num2
num2。
n
u
m
1
num1
num1序列元素为3 2 1 4 5
;
n
u
m
2
num2
num2序列元素为1 2 3 4 5
。
3 2 1 4 5
1 2 3 4 5
我们定义一个数组
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示到
n
u
m
1
num1
num1数组第
i
i
i项和到
n
u
m
2
num2
num2数组第
j
j
j项的最大公共子序列的长度。
我们
d
p
dp
dp数组得到的子结构就是
d
p
[
i
]
[
j
]
=
{
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
n
u
m
1
[
i
]
!
=
n
u
m
2
[
j
]
d
p
[
i
−
1
]
[
j
−
1
]
+
1
n
u
m
1
[
i
]
=
n
u
m
2
[
j
]
dp[i][j] =\begin{cases} max(dp[i-1][j],dp[i][j-1]& {num1[i] != num2[j]} \\ dp[i-1][j-1]+1 &num1[i] = num2[j]\end{cases}
dp[i][j]={max(dp[i−1][j],dp[i][j−1]dp[i−1][j−1]+1num1[i]!=num2[j]num1[i]=num2[j]
由此可得如下表得到的上述例子 d p dp dp数组
# | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | 0 | 0 | 1 | 1 | 1 |
2 | 0 | 1 | 1 | 1 | 1 |
3 | 1 | 1 | 1 | 1 | 1 |
4 | 1 | 1 | 1 | 2 | 2 |
5 | 1 | 1 | 1 | 2 | 3 |
代码
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
#define mem(s,i) memset(s,i,sizeof(s))
#define INF 0x7fffffff
using namespace std;
const int N = 1e5+5;
int dp[1005][1005];
int num1[1005], num2[1005];
int max(int x, int y){
return x>y?x:y;
}
void solve(){
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i++)scanf("%d",&num1[i]);
for(int i = 1; i <= n;i++)scanf("%d",&num2[i]);
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
if(num1[i]==num2[j])dp[i][j] = dp[i-1][j-1]+1;
else dp[i][j] = max(dp[i][j-1],dp[i-1][j]);
}
}
printf("%d\n",dp[n][n]);
}
int main(){
solve();
return 0;
}
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)方法
思路
上述
O
(
n
2
)
O(n^{2})
O(n2)的方法还不是最佳的方法,遇到序列大小为
1
0
5
10^{5}
105就会超时。
所以就有大佬想到了将
L
C
S
LCS
LCS问题变成
L
I
S
LIS
LIS问题的方法。
举个栗子,还是序列
n
u
m
1
num1
num1和
n
u
m
2
num2
num2。
num1:3 2 1 4 5
num2:1 2 3 4 5
假设
3
=
a
,
2
=
b
,
1
=
c
,
4
=
d
,
5
=
e
3=a, 2 = b, 1 = c, 4 = d, 5 = e
3=a,2=b,1=c,4=d,5=e
那么得到两个新序列
num1: a b c d e
num2: c b a d e
我们可以看到这两个序列的子序列一定存在于
n
u
m
1
num1
num1序列中,而新
n
u
m
1
num1
num1序列是递增的,那么这个子序列一定也是递增的,那么换句话说, 在
n
u
m
2
num2
num2中递增的序列一定是
n
u
m
1
num1
num1的子序列。而要求的最长的公共子序列也变为为
n
u
m
2
num2
num2的最长非递减子序列,也就是最长上升子序列。
我们定义一个
m
a
p
map
map数组表示
n
u
m
1
num1
num1个各元素输入的对应顺序(或位置)。
然后求
m
a
p
[
n
u
m
2
[
i
]
]
map[num2[i]]
map[num2[i]]的最大上升子序列。
m
a
p
[
n
u
m
2
[
i
]
]
map[num2[i]]
map[num2[i]]是
n
u
m
2
num2
num2元素在
n
u
m
1
num1
num1中的位置。
求最长上升子序列的
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)方法
代码
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
#define mem(s,i) memset(s,i,sizeof(s))
#define INF 0x7fffffff
using namespace std;
const int N = 1e5+5;
int num1[N], num2[N], map[N], f[N];
void solve(){
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i++){scanf("%d",&num1[i]);map[num1[i]] = i;}
for(int i = 1;i <= n;i++){scanf("%d",&num2[i]);f[i] = INF;}
int len = 0;
f[0] = 0;
for(int i = 1;i <= n;i++){
int l = 0, r = len, mid;
if(map[num2[i]] > f[len]){f[++len] = map[num2[i]];}
else{
while(l < r){
mid = (l + r)>>1;
if(map[num2[i]]>=f[mid]){
l = mid+1;
}
else{
r = mid;
}
}
f[l] = min(f[l],map[num2[i]]);
}
}
// for(int i = 0;i <= n;i++){
// printf("%d ",f[i]);
// }
printf("%d\n",len);
}
int main(){
solve();
return 0;
}