1. 程式人生 > >HYSBZ 1264 【AHOI2006】基因匹配Match

HYSBZ 1264 【AHOI2006】基因匹配Match

題目大意:求兩個均只含有1-N且每個數字都恰好有5個的排列的最長公共子序列。

普通的dp方法時間複雜度為O(n^2),對本題的資料量來說並不適用。

有這麼一個結論:求兩個序列的最長公共子序列,等價於求每個位置的數字在另一個序列中的所有位置的逆序組成的序列的最長上升子序列。

舉個例子:
序列1:1 1 1 1 1
序列2:1 1 1 1 1
序列1中的每個位置的數字在序列2中所有位置的逆序均為(5,4,3,2,1),則組成的新的位置序列為
5 4 3 2 1 5 4 3 2 1 5 4 3 2 1 5 4 3 2 1 5 4 3 2 1
求出該序列的最長上升子序列為5,即原問題的最長公共子序列為5。

當然,求最長上升子序列也有時間複雜度為O(n^2)

的dp方法和二分優化後的O(n*log(n))方法,本題顯然需要後一種方法才能解決。
假設pos陣列存新組成的位置序列,dp[i]表示長度為i的結尾數字的最小值,len代表當前的最大長度,易知dp值具有單調性。遍歷pos序列,通過二分查詢,若當前的pos值大於dp[len],則dp[++len]=pos;否則更新第一個結尾數字大於等於pos的dp值為pos。

稍微思考一下,由上面轉換成位置序列的過程可知,兩個序列中重複數字的個數越多,最後得到的位置序列數目也就越多,最壞情況是N個數全相同,則位置序列有N*N個數,時間複雜度退化為O(n^2*log(n^2));最好的情況則是同一個序列中每個數字均不相同,位置序列數也為N,時間複雜度為O(n*log(n))

程式碼如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define For(i,a,b) for(int i=a;i<=b;i++)
#define INF 0x3f3f3f3f
#define db double
#define ldb long double
#define m_p make_pair
#define p_b push_back
#define fbo friend bool operator <
const int N = 20005;
const int mod=1e9+7;
struct FastIO//輸入掛
{
    static const int S=200;
    int wpos;
    char wbuf[S];
    FastIO():wpos(0){}
    inline int xchar()
    {
        static char buf[S];
        static int len=0,pos=0;
        if(pos==len) pos=0,len=fread(buf,1,S,stdin);
        if(pos==len) exit(0);
        return buf[pos++];
    }
    inline int read()
    {
        int s=1,c=xchar(),x=0;
        while(c<=32) c=xchar();
        if(c=='-') s=-1,c=xchar();
        for(;'0'<=c&&c<='9';c=xchar()) x=x*10+c-'0';
        return x*s;
    }
    ~FastIO()
    {
        if(wpos) fwrite(wbuf,1,wpos,stdout),wpos=0;
    }
}io;
int n,a1[5*N],a2[5*N],a[5*5*N],dp[5*5*N];
vector<int> v[N];

int main(){
//	freopen("1.txt","r",stdin);
	n=io.read();n=5*n;
	For(i,1,n) a1[i]=io.read();
	For(i,1,n) a2[i]=io.read();
	for(int i=n;i>=1;i--)v[a2[i]].p_b(i);
	For(i,1,n){
		For(j,1,5) a[(i-1)*5+j]=v[a1[i]][j-1];
	}
	int len=1;dp[len++]=a[1];
	For(i,2,5*n){
		int pos=lower_bound(dp+1,dp+len,a[i])-dp;
		if(pos<len) dp[pos]=a[i];
		else dp[len++]=a[i];
	}
	printf("%d\n",len-1);
	return 0;
}