原题链接
题目大意
有两串 n ( 1 ≤ n ≤ 100000 ) n(1\le n\le 100000) n(1≤n≤100000)个数字的串(每个数都 ≤ 100000 \le 100000 ≤100000),要求求出两个串的最长公共子串的长度。
解题思路
这题,如果使用最长公共子串的方法,时间复杂度将会达到
O
(
n
2
)
O(n^2)
O(n2),也就是
O
(
10000000000
)
O(10000000000)
O(10000000000),所以很明显,这题要换一种做法。深入思考后,会发现这题可以转换为最长不下降子序列来做。我们可以将数字的大小重新定义,将大小关系定义为第二个序列的下标大小。如,在“
10
,
9
,
8
,
7
,
6
,
5
,
4
,
3
,
2
,
1
10,9,8,7,6,5,4,3,2,1
10,9,8,7,6,5,4,3,2,1”和“
10
,
9
,
8
,
7
,
6
,
5
,
4
,
3
,
2
,
1
10,9,8,7,6,5,4,3,2,1
10,9,8,7,6,5,4,3,2,1”中,
10
<
9
<
8
<
7
<
6
<
5
<
4
<
3
<
2
<
1
10<9<8<7<6<5<4<3<2<1
10<9<8<7<6<5<4<3<2<1。使用第二个串的大小关系,对第一个串进行最长不下降子序列来处理,得到的子串长度,就是最长公共子串的长度。但这时,稍加计算会发现,这种做法的时间复杂度将会也达到
O
(
n
2
)
O(n^2)
O(n2)。怎么优化呢?我们可以使用贪心,如果可以,我们就把它放在结尾,如果不行,就在队列里找到一个位置,是比它小的最大数(尽量大,这样就可以让队列尽量长),并把它替换。
如:
10
,
1
,
2
,
3
,
9
,
8
,
7
,
6
,
5
,
4
10,1,2,3,9,8,7,6,5,4
10,1,2,3,9,8,7,6,5,4
10
,
9
,
8
,
7
,
6
,
5
,
4
,
3
,
2
,
1
10,9,8,7,6,5,4,3,2,1
10,9,8,7,6,5,4,3,2,1
将第一个序列中的10放入队列中,由于数列是空的,所以直接放在末尾,并把长度加一(10);
将第一个序列中的1放入队列中,由于数列的末尾的下标比1小,所以直接放在末尾,并把长度加一(10,1);
将第一个序列中的2放入队列中,但数列的末尾的下标比2大,我们需要从数量中找到比它小的最大的数,并把它替换掉(长度不用加)(10,2);
将第一个序列中的3放入队列中,但数列的末尾的下标比3大,我们需要从数量中找到比它小的最大的数,并把它替换掉(长度不用加)(10,3);
将第一个序列中的9放入队列中,但数列的末尾的下标比9大,我们需要从数量中找到比它小的最大的数,并把它替换掉(长度不用加)(10,9);
将第一个序列中的8放入队列中,由于数列的末尾的下标比8小,所以直接放在末尾,并把长度加一(10,9,8);
将第一个序列中的7放入队列中,由于数列的末尾的下标比7小,所以直接放在末尾,并把长度加一(10,9,8,7);
将第一个序列中的6放入队列中,由于数列的末尾的下标比6小,所以直接放在末尾,并把长度加一(10,9,8,7,6);
将第一个序列中的5放入队列中,由于数列的末尾的下标比5小,所以直接放在末尾,并把长度加一(10,9,8,7,6,5);
所以,最长公共子串的长度为6(注意:那个队列的长度是最长公共子串的长度,但那个队列不是最长公共子串!),如果用上二分,那时间复杂度将会变成
O
(
n
∗
l
o
g
n
)
O(n*logn)
O(n∗logn);
代码实现
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<ctime>
#include<set>
#include<ios>
using namespace std;
long long b[200000],x,c[200000],s[200000],n,ans;
int serch(long long x)//二分查找
{
int l=1,r=ans;
while(l<r){
int mid=(l+r)/2;
if(s[c[mid]]>s[x])
r=mid;
else
l=mid+1;//往大找
}
return l;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
cin>>b[i];
for(int i=1;i<=n;i++){
cin>>x;
s[x]=i;
}//大小定义数组
for(int i=1;i<=n;i++){
if(s[b[i]]>s[c[ans]])
c[++ans]=b[i];//可以放在末尾,直接放
else
c[serch(b[i])]=b[i];//不能,使用二分
}
cout<<ans;
}
样例
输入
7
1 2 3 4 5 6 7
7 6 5 4 1 2 3
输出
3