2666.勇闯黄金十二宫射手宫

原题链接

外网进不去

题目大意

有两串 n ( 1 ≤ n ≤ 100000 ) n(1\le n\le 100000) n(1n100000)个数字的串(每个数都 ≤ 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 10123987654
10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 10,9,8,7,6,5,4,3,2,1 10987654321
将第一个序列中的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(nlogn);

代码实现

#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

おすすめ

転載: blog.csdn.net/weixin_41247488/article/details/119834709