习题3-7 DNA序列(DNA Consensus String, ACM/ICPC Seoul 2006, UVa1368)

该博客介绍了如何解决ACM/ICPC Seoul 2006和UVa1368题目的问题,即给定多个DNA序列,找到一个序列使其与所有输入序列的Hamming距离之和最小,并且结果序列字典序最小。通过分析,博主提出了一种方法,通过计算每个位置上出现次数最多的碱基,来确定最终的DNA序列。在实现过程中,利用ACGT的字典序避免了额外的排序步骤,同时记录每个位置的碱基出现次数,从而得到最小Hamming距离和的解决方案。

题目描述:输入m个长度均为n的DNA序列,求一个DNA序列,到所有序列的总Hamming距离尽量
小。两个等长字符串的Hamming距离等于字符不同的位置个数,例如,ACGT和GCGA的
Hamming距离为2(左数第1, 4个字符不同)。
                  输入整数m和n(4≤m≤50, 4≤n≤1000),以及m个长度为n的DNA序列(只包含字母
A,C,G,T),输出到m个序列的Hamming距离和最小的DNA序列和对应的距离。如有多
解,要求为字典序最小的解。例如,对于下面5个DNA序列,最优解为TAAGATAC。
TATGATAC
TAAGCTAC
AAAGATCC
TGAGATAC
TAAGATGT

题目分析:题中引入了Hamming距离的定义,看似要与m个序列比较求最小和十分麻烦,实际上用通俗的语言描述就是:求每个序列的对应位置出现次数的最多的字母,然后输出。题目的描述其实就是这个意思,分别找出每一列哪个元素重复次数最多。输入的m行n列元素用二维数组a存储,为了方便引入另一个数组b,其值为a的'转置'(行和列互换),这样每次需要比较的元素就在一行中,方便放入函数的参数中(因为需要循环多次,故将求重复次数最多的字母的方法放入函数中),因为要计算次数并且求和,于是再引入一个数组q,其中存放的是每一列ACGT出现的次数,注意ACGT对应q[1]q[2]q[3]q[4],即按字典序从小到大的顺序存放,将ACGT按顺序对应起来之后,后面就不用再计算字典序的大小了。次数存放好后,找出数组q中最小的元素对应的下标,注意用从大到小逐个比较,这样小的数优先级在前,即就算有两个元素值相等,较小的也会覆盖之前的,对应到ACGT就是A的会覆盖CGT的,这样就实现了字典序最小,不用再用多余的代码去算字典序。由于还要求最小距离和,故引用传参传入累加量,其值为数组b的列数,也就是DNA序列的个数m减去求出的最大出现次数,之后在函数外累加即可。函数最后就将出现次数对应回字母并返回。

 代码如下:

//TEST
/*5 8
T A T G A T A C
T A A G C T A C
A A A G A T C C
T G A G A T A C
T A A G A T G T
RESULT
TAAGATAC
7
*/
#include <iostream>
using namespace std;
int findmin(char *x,int len,int &y){//注意要传入元素个数确定界限,累加量要用引用传入
    int m=0,n,q[5]={0};
    for(int k=0;k<len;k++)
        switch(x[k]){
            case 'A':
                q[1]++;
                break;
            case 'C':
                q[2]++;
                break;
            case 'G':
                q[3]++;
                break;
            case 'T':
                q[4]++;
                break;
        }

    for(int i=4;i>0;i--)
        if(q[i]>m) {m=q[i];n=i;}//求出出现字数最多的字母,同时计算了其中最小的字典序
    y=len-q[n];//距离累加量
    switch(n){//最后对应回去,返回字符
        case 1:
            return 'A';
            break;
        case 2:
            return 'C';
            break;
        case 3:
            return 'G';
            break;
        case 4:
            return 'T';
            break;
    }
}
int main()
{
    int m,n,s,sum=0;
    char a[55][1010],b[55],c[1010][55];
    cin>>m>>n;
    for(int i=0;i<m;i++)
        for(int j=0;j<n;j++)
        {
            cin>>a[i][j];
            c[j][i]=a[i][j];//c数组即将a数组的行列互换,方便比较
        }
    for(int i=0;i<n;i++)
    {
        b[i]=findmin(c[i],m,s);//该函数将返回每一列出现次数最多的字母
        sum+=s;//每一列都需要累加距离
    }

    for(int i=0;i<n;i++)
    cout<<b[i];
    cout<<endl;
    cout<<sum<<endl;
    return 0;
}

总结:

一开始是打算先求出距离最小和序列的,然后再计算字典序,但发现这样十分麻烦,而在计算最小和的过程中,发现可以巧妙的利用ACGT的顺序从而使求出的序列一定为字典序最小的序列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值