Written with StackEdit.
Description
卡卡昨天晚上做梦梦见他和可可来到了另外一个星球,这个星球上生物的(DNA)序列由无数种碱基排列而成(地球上只有(4)种),而更奇怪的是,组成(DNA)序列的每一种碱基在该序列中正好出现(5)次!这样如果一个(DNA)序列有(N)种不同的碱基构成,那么它的长度一定是(5N)。
任务:编写一个程序:
- 从输入文件中读入两个等长的(DNA)序列;
- 计算它们的最长公共子序列长度;
- 向输出文件打印你得到的结果。
Input
输入文件中第一行有一个整数(N),表示这个星球上某种生物使用了N种不同的碱基,以后将它们编号为(1…N)的整数。
以下还有两行,每行描述一个(DNA)序列:包含(5N)个(1…N)的整数,且每一个整数在对应的序列中正好出现(5)次。
Output
输出文件中只有一个整数,即两个(DNA)序列的最长公共子序列长度。
Sample Input
2
1 1 2 2 1 1 2 1 2 2
1 2 2 2 1 1 2 2 1 1
Sample Output
7
HINT
[数据约束和评分方法]
(60\%)的测试数据中:(1<=N <= 1000.)
(100\%)的测试数据中:(1<=N <= 20000.)
Solution
- 朴素的(O(n^2)dp)只能解决(60\%)的部分.
- 注意到一个关键性质,每个数在每个序列中都恰好出现(5)次.
- 类似于给两个排列求(LCS),我们可以记录下每个数字在第一个序列(a)中从前往后的(5)个位置.
- 再处理第二个序列(b),此时我们处理当前数(b[i])在第一个序列中每个出现的位置(pos).有(a[pos]=b[i].)
- 那么根据朴素(dp)的思路,定义(f[i])为只用(a)序列的前(i)个数的(LCS)长度.
- 此时就可以完成更新(f[pos]=max(f[1)~(pos-1])+1).利用树状数组维护最大值进行优化.
- 特别注意:每个(b[i])对应了(5)个(pos),而我们处理它们的时候应该从后往前倒序处理,否则先更新的会对后面的造成影响,破坏无后效性(类似01背包).
#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
{
fh=-1;
jp=getchar();
}
while (jp>='0'&&jp<='9')
{
out=out*10+jp-'0';
jp=getchar();
}
return out*fh;
}
const int MAXN=2e5+10;
int ta[MAXN];
int va[MAXN][10];
int bit[MAXN];
int n;
inline void upd(int x,int c)
{
for(;x<=n;x+=lowbit(x))
bit[x]=max(bit[x],c);
}
inline int query(int x)
{
int res=0;
for(;x;x-=lowbit(x))
res=max(res,bit[x]);
return res;
}
int main()
{
n=read();
n*=5;
for(int i=1;i<=n;++i)
{
int t=read();
va[t][++ta[t]]=i;
}
for(int i=1;i<=n;++i)
{
int t=read();
for(int j=5;j>=1;--j)//值得细细斟酌
{
int pos=va[t][j];
int mx=query(pos-1)+1;
upd(pos,mx);
}
}
int ans=query(n);
printf("%d
",ans);
return 0;
}