序列问题III | ||||||
| ||||||
Description | ||||||
有俩个长度分别为p和q的序列A和B,每个序列的各个元素互不相同,且每个元素的大小都是1~(p和q中的最大值)之间的正整数。 俩个序列的第一个元素都为1,求出A和B的最长公共子序列长度。 | ||||||
Input | ||||||
输入第一行为数据组数T(T<=20)。每组数据包括3行,第一行为2个整数p和q(1<=p,q<=10000), 第二行包含序列A,其中第一个数为1。 第三行包含序列B,格式同序列A。 | ||||||
Output | ||||||
对于每组数据,输出A和B的最长公共子序列的长度。 | ||||||
Sample Input | ||||||
2 3 2 1 2 3 1 2 7 8 1 7 5 4 8 3 2 1 4 3 5 6 2 8 7 | ||||||
Sample Output | ||||||
2 4 | ||||||
Hint | ||||||
对于第二组数据最长公共子序列为1,4,3,2。 | ||||||
Author | ||||||
陈禹@HRBUST |
思路:
最长公共子序列问题,Dp解法时间复杂度O(n^2);
显然空间和时间都是不允许的,那么考虑将问题转化为最长上升子序列问题。
对应将a序列的每个元素的位子进行记录,那么对于b序列需要匹配的位子就是pos【b【i】】;
那么显然匹配过程中不能有交叉的线,那么一定就是要求一个最长上升子序列问题。
最长上升子序列问题可以Nlogn来解。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<map>
#include<algorithm>
using namespace std;
int a[1000800];
int f[1000800];
int Slove(int n)
{
int c=0;
for(int i=1; i<=n; i++)
{
int t=a[i];
if(i==1) f[++c]=t;
else
{
if(t>f[c]) f[++c]=t;
else
{
int pos=lower_bound(f+1,f+c,t)-f;//二分找到数组中比t大的第一个元素的的地址。
f[pos]=t;
}
}
}
return c;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
map<int ,int >pos;
for(int i=1; i<=n; i++)
{
int x;
scanf("%d",&x);
pos[x]=i;
}
for(int i=1; i<=m; i++)
{
int x;
scanf("%d",&x);
if(pos[x]==0)a[i]=-1;
else
a[i]=pos[x];
}
printf("%d\n",Slove(m));
}
}