基因匹配Match(最长公共子序列) NlongN

来源:互联网 发布:java rsa加密 编辑:程序博客网 时间:2024/06/01 21:55

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82285#problem/F

题意:两个串,求最长公共子序列,有n个数,每个数都出现5次。设为a串和b串 

分析:首先n<=20000,5*n=10^5 如果用普通的方法(O(N^2)),肯定会超时,因此需要优化。普通状态转移方程若a[i]==b[i]:dp[i][j]=dp[i-1][j-1]+1;否则,dp[i][j]=max(dp[i-1][j],dp[i][j-1]);这样每次遍历浪费了大量的时间

          我们可以把a串作为模式串,b串作为查找串。由于这里每个数只有5次,我们可以先存下a串中的每个数在b串中的5个数的位置,用f[a[i]][j]表示,a[i]这个数在b串中的id为j. 所以每次从a串中加入一个数字后,只需要遍历(这5个数要从后向前遍历,否则从前往后的话,计算后一个数会算重)这个数字在b串中的5个位置就可以更新到下一个状态才+1(因为找的就是公共子序列,只有当两个字母相等才+1)。

          这样我们可以开个c[20000*5+1]的辅助数组,c[j]表示,当前,b串中的第j位置的数,最大可以与a串公共子序列的长度。当遍历a串的时候,对应b的5个数从后往前更新c[j],c[j]=max(c[1]......c[i-1])+1。那么,我们可以用线段数或树状数组求c[j]之前的最大值。当a串遍历完后,找出c串的最大值就是ans。

#include <iostream>#include <stdio.h>#include <math.h>#include <algorithm>#include <queue>#include <stack>#include <vector>#include <string>#include <string.h>#include <map>#include <set>using namespace std;#define inff 1000000000#define maxn 20010int a[maxn*5],b[5*maxn],f[5*maxn][6];struct node{    int l,r,m;}tree[5*maxn*4];void bulid(int id,int l,int r){    tree[id].l=l;    tree[id].r=r;    tree[id].m=0;    if (l!=r)    {        int mid=(l+r)/2;        bulid(id*2,l,mid);        bulid(id*2+1,mid+1,r);    }}void pushup(int id){    tree[id].m=max(tree[id*2].m,tree[id*2+1].m);}void update(int id,int pos,int val){    if(tree[id].l==tree[id].r)    {        tree[id].m=val;    }    else    {        int mid=(tree[id].l+tree[id].r)/2;        if(pos<=mid)            update(id*2,pos,val);        else if(pos>mid)            update(id*2+1,pos,val);        pushup(id);    }}int query(int id,int l,int r){    if(l<=tree[id].l&&tree[id].r<=r)    {        return tree[id].m;    }    else    {        int mid=(tree[id].l+tree[id].r)/2;        if(r<=mid)            return query(id*2,l,r);        else if(l>mid)            return query(id*2+1,l,r);        else        {            int a=query(id*2,l,r);            int b=query(id*2+1,l,r);            return max(a,b);        }    }}int main(){    int i,n,j;    while(scanf("%d",&n)!=EOF)    {        for(i=1;i<maxn;i++)        {            for(j=1;j<=5;j++)            {                f[i][j]=-1;            }        }        for(i=1;i<=5*n;i++)        {            scanf("%d",&a[i]);        }         for(i=1;i<=5*n;i++)        {            scanf("%d",&b[i]);            for(j=1;j<=5;j++)            if(f[b[i]][j]==-1)            {                f[b[i]][j]=i;break;//存下这个数的出现的5个id            }        }        bulid(1,1,n*5);        int maxx;        for(i=1;i<=5*n;i++)//遍历a        {            for(j=5;j>=1;j--)//从后向前遍历这个数的5个id              {                  if(f[a[i]][j]==1)//如果id==1,它的最大值肯定为1                  {                      update(1,f[a[i]][j],1);continue;                  }                   maxx=query(1,1,f[a[i]][j]-1);                  update(1,f[a[i]][j],maxx+1);              }        }        printf("%d\n",query(1,1,n*5));       // printf("%d\n",tree[1].m);    }    return 0;}


0 0
原创粉丝点击