【题解】密码

题目描述

  从汉中回来后,pear编制了一本教义问答手册。为了防止别人盗窃这本价值连城的手册,pear决定与bx2k一起编密码来将手册加密。

  pear的编制密码方法是这样的,他先让他自己和bx2k选各一个1-n的排列,然后pear会将这两个排列的最长公共子序列作为密码。bx2k想知道这个密码的长度,又不想告诉你密码,于是他就把这两个排列做了适当改动后来问你。

输入输出格式

输入格式

  第一行包含一个数n。

  第二行包含一个1-n的排列,代表适当改动后的bx2k的排列。

  第三行包含一个1-n的排列,代表适当改动后的pear的排列。

输出格式

  一行,第一行包含一个数ans,代表这两个排列的最长公共子序列的长度。

输入输出样例

输入样例

5

4 5 1 2 3

1 4 2 5 3

输出样例

3

说明

样例说明

  {1,2,3}为一个最长公共子序列,长度为3。

数据规模

  对于30%的数据,n≤5;

  对于70%的数据,n≤10^3

  对于100%的数据,n≤10^5

题解

  直接用dp求LCS的时间复杂度很大,为O(n^2),我们要换一种做法。

  因为这两个序列的元素是相同的,我们可以用一个pos数组储存a[i]在b序列的位置。

  由于公共子序列是要求位置保持升序的,我们就可已转换成求pos数组的LIS了。

  现在就可以用树状数组等数据结构来优化dp的,但是下面我要介绍一种贪心做法(应该是)。

  此时开一个st数组来装LIS。

  根据贪心,我们可以得到:st前面尽可能小,st后面就能尽可能进入新的元素,我们只需要不断更新已有的LIS就行了。

  因为st满足单调性,所以用二分优化就可以把时间复杂度降到O(nlogn)。

#include <iostream>
#include <cstdio>

#define MAX_N 100000

using namespace std;

int n;
int a[MAX_N + 5];
int b[MAX_N + 5];
int pos[MAX_N + 5];
int s[MAX_N + 5], cnt;

int main()
{
    scanf("%d", &n);
    for(register int i = 1; i <= n; ++i)
    {
        scanf("%d", a + i);
    }
    for(register int i = 1; i <= n; ++i)
    {
        scanf("%d", b + i);
    }
    for(register int i = 1; i <= n; ++i)
    {
        pos[b[i]] = i;
    }
    int lt, rt, mid;
    for(register int i = 1; i <= n; ++i)
    {
        if(pos[a[i]] > s[cnt])
        {
            s[++cnt] = pos[a[i]];
            continue;
        }
        lt = 1, rt = cnt;
        while(lt < rt)
        {
            mid = lt + rt >> 1;
            if(pos[a[i]] < s[mid]) rt = mid;
            else lt = mid + 1;
        }
        s[rt] = pos[a[i]];
    }
    printf("%d", cnt);
    return 0;
}
参考程序

猜你喜欢

转载自www.cnblogs.com/kcn999/p/10462686.html