【SCMS】最短公共母序列的代码实现

前言

其实说起初衷,是之前吾辈的学校开展了个数模比赛,我去帮别人当帮手,当时的B题是基因拼接,D题是污染程度评定

作为ACMer,我当然喜欢B题这种像ACM竞赛题一样感觉的东西,但无奈人家才是参(da)赛(ye)者(a),他们选了D,嘛,其实据说那次我做的也还是不错。

回到正题,于是我就开始考虑这种算法改如何实现呢,反正现在是学生,在CNKI上看论文不要钱,到处看看咯。

看到个论文,用dp实现生物序列最短公共超序列,感觉靠谱,要不然就试试呗,拿来当模板用,说不定哪天就出道题呢~

给他起了个名儿偷笑

SCMS = Shortest Common Mother-Sequence (最短公共母序列)

建模

可以将题意理解为ACM格式,则为如下情况:

数据组数为T,【给出T】

每组数据中有n条基因序列,【给出N】

以下则为n行,每行以一个字符串表示一条基因序列的组成【给出str_i】

【每条基因序列均为母序列中的一个子片段,此处的子片段指的是子串而非子序列】(给自己降低一点难度嘛不要在意)

要求输出满足所有这些基因序列的母序列。


代码实现(C/C++)

#include<stdio.h>
#include<string.h>

struct str
{
    char letter[1600];
    int len;
}ans,a[20];

struct
{
    struct str temp;
    int x,y;
}add[500];

int f[1<<12][15],father[1<<12][15],reduce[20][20],cost[20][20],n,m,max,Test,s;
struct str ADD(struct str ans,int k,int reduce)
{
    int i;
    for(i=ans.len;i<ans.len+a[k].len-reduce;i++)
        ans.letter[i]=a[k].letter[reduce+i-ans.len];
    ans.letter[i]=0;
    ans.len=ans.len+a[k].len-reduce;
    return ans;
}
void quick(int l,int r)
{
    int i=l,j=r;
    struct str x=add[(i+j)>>1].temp;
    while(i<=j)
    {
        while(strcmp(add[i].temp.letter,x.letter)<0)  i++;
        while(strcmp(add[j].temp.letter,x.letter)>0)  j--;
        if(i<=j)
        {
            add[0]=add[i],add[i]=add[j],add[j]=add[0];
            i++,j--;
        }
    }
    if(i<r)  quick(i,r);
    if(j>l)  quick(l,j);
}
void READY()
{
    int i,j,k,r;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            for(k=0;k<a[i].len;k++)
            {
                for(r=0;k+r<a[i].len&&a[i].letter[k+r]==a[j].letter[r];r++);
                if(k+r==a[i].len)
                {
                    reduce[j][i]=r;
                    break;
                }
            }
    s=0;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        {
            s++;
            add[s].temp=ADD(a[i],j,reduce[j][i]),add[s].x=i,add[s].y=j;
        }
    quick(1,s);
    for(i=1;i<=s;i++)
        cost[add[i].x][add[i].y]=i;
}
void read()
{
    int i,j,k,r;
    scanf("%d",&n);
    memset(reduce,0,sizeof(reduce));
    for(i=1;i<=n;i++)
    {
        scanf("%s",a[i].letter);
        a[i].len=strlen(a[i].letter);
    }
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        {
            if(i==j)  continue;                 
            for(k=0;k<a[j].len;k++)
            {
                for(r=0;r+k<a[j].len&&r<a[i].len&&a[j].letter[k+r]==a[i].letter[r];r++);
                if(r==a[i].len)  break;
            }
            if(k<a[j].len)  a[i].letter[0]=0;
        }
    for(i=n;i>=1;i--)
        if(a[i].letter[0]==0)
        {
            for(j=i;j<n;j++)
                a[j]=a[j+1];
            n--;
        }
    m=(1<<n)-1;
}
void DP()
{
    int i,j,k;
    for(i=0;i<=m;i++)
        for(j=1;j<=n;j++)
            f[i][j]=father[i][j]=0;
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
        {
            int add=1<<(j-1);
            if((i&add)==0)
                for(k=1;k<=n;k++)
                    if((i&(1<<(k-1)))!=0)
                    {
                        if(father[i+add][j]==0)
                        {
                            f[i+add][j]=f[i][k]+reduce[k][j],father[i+add][j]=k;
                            continue;
                        }
                        if(f[i+add][j]<f[i][k]+reduce[k][j])
                            f[i+add][j]=f[i][k]+reduce[k][j],father[i+add][j]=k;
                        else if(f[i+add][j]==f[i][k]+reduce[k][j]&&cost[j][father[i+add][j]]>cost[j][k])
                            father[i+add][j]=k;
                    }
        }
}
void solve()
{
    max=-1;
    int i,j,now;
    for(i=1;i<=n;i++)
    {
        if(max<f[m][i])
        {
            max=f[m][i],now=m;
            ans=a[i];
            for(j=i;father[now][j]!=0;)
            {
                ans=ADD(ans,father[now][j],reduce[father[now][j]][j]);
                int t=j;
                j=father[now][j],now-=(1<<(t-1));
            }
        }
        else if(max==f[m][i])
        {
            struct str temp=a[i];
            now=m;
            for(j=i;father[now][j]!=0;)
            {
                temp=ADD(temp,father[now][j],reduce[father[now][j]][j]);
                int t=j;
                j=father[now][j],now-=(1<<(t-1));
            }
            if(strcmp(ans.letter,temp.letter)>0)  ans=temp;
        }
    }
}
int main()
{
    int T=0;
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
    {
		read();
		READY();
		DP();
		solve();
		printf("%s\n",ans.letter);
    }
    return 0;
}


Reference

参阅的论文如下,关于证明过程和各种条件约束详见论文:

 《两个生物序列最短公共超序列的动态规划算法》

黄永莲1, 孙世军2

 (1.湛江师范学院生物科学与化学学院,广东湛江524048; 

  2.湛江师范学院信息科技学院,广东湛江524048)

摘要:将动态规划算法用于两个生物序列的最短公共超序列的计算。

计算过程分为两个算法,第一个算法计算两个序列的所有前缀的最短公共超序列的长度,

并存放在一个矩阵中,第二个算法利用前面所得的矩阵,找到两个序列的最短公共超序列。

关键词:生物序列;动态规划算法;超序列;最短公共超序列

中图分类号:Q-31   文献标识码:A   文章编号:1671-0231(2005)03-0063-03

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糖果天王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值