《算法竞赛入门经典(第2版)》习题3-7DNA序列(DNA Consensus string,UVa1368)

这个问题如果简化的话需要比较另类的思路,题目的意思是先求hamming距离,然后提取出hamming距离最小的序列,如果满足该条件的序列数大于1,则求字典序最小的序列。这里注意,字典序最小序列可能是输入序列,但也可能是根据输入序列重新生成的序列。

常规思路:(输入三个序列,AGCAT、ATCGG、GATTA)
1)先遍历求出每个序列对应的hamming距离,具体是通过三个循环实现,第一个循环选择被研究序列,例如选择AGCAT,这里声明一个hamming距离,第二个循环选择比较序列,例如ATCGG,这里声明一个局部求和值h,第三个循环按位比较,不匹配h值加一,再加到hamming距离中完成一个序列的hamming距离计算,计算完毕后将距离存储到数组中,数组编号同字符数组行编号一致。
2)找最值,找出hamming距离最小值对应的int数组编号,如果最小值只有一个,那直接输出序列和hamming距离,如果大于一则存储该hamming距离对应的全部字符串的行编号,在这些字符串中找字典序最小。
3)字典序最小:用两个循环表示,大循环按列,小循环按行,以上述序列为例,AGCAT和ATCGG的hamming距离最小都是8。首先,第一列第一行是A,第一列第二行也是A,比较大小发现A是最小的,那输出这个字符;随后,第二列第一行是G,第二列第二行是T,比较发现G是最小的,输出字符G,以此类推,每个小循环会输出一个字符,而所有的这些字符需要被存储起来生成一个新的序列,为什么?因为字典序最小的序列可能在输入中不存在,比如本例字典序最小为AGCAG,这个hamming距离没有被算过,所以还得重算一遍hamming距离求最小值,最后输出这个序列和最小值。
常规思路最难受的一点就是复杂,求hamming的过程被重复了两次,整体算法的时间复杂度可以计算一下,不过实践中时间倒不是什么问题,主要是bug,整体思路复杂后bug奇多,这可不是好事,常规思路对于大输入环境也不友好,所以需要简化算法。

简化思路:(输入三个序列,AGCAT、ATCGG、GATTA)
1)按列为大循环,按行为小循环,统计各个字符每列出现的个数,例如第一列第一行A出现了一次,第一列第二行A出现了一次,第一列第三行G出现了一次。
2)在按列的大循环中找到每一列的最大重复的字符和重复次数,最小重复字符和重复次数,例如第一列A重复了2次,最多,G重复了1次,最小。
3)计算hamming距离,例如A重复的最多,重复了2次,那hamming距离就是3-2=1了
那如果各出现一个,比如G一次,T一次,A一次怎么办,这个优先级设置就很有意思了,之前设定计数器flag,如果出现A,flag[0]++,出现C,flag[1]++,求最大重复次数的循环是从0开始的,所以如果所有元素出现一次,那min和max会锁定在A上,优先级的问题解决。最后按列逐字输出即可。
我把代码放出来让大家看看两种算法的复杂程度:

常规算法:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
char aa[60][10000];
int  bb[60];
int  cc[60000];
char dd[60000];
int flag=0;
int min=0;
void oper(int m,int n)
{
    for(int i=0;i<m;i++)
    {   int sum=0;
        for(int j=0;j<m;j++)
        {   int ham=0;
            if(!strcmp(aa[i],aa[j])){ham=0;}
            if(strcmp(aa[i],aa[j])!=0)
            {
                for(int k=0;k<n;k++)
                {
                    if(aa[i][k]!=aa[j][k]) {ham=ham+1;}
                    if(aa[i][k]==aa[j][k]) {continue;}
                }
            }
            sum=sum+ham;
            bb[i]=sum;
        }
    }
    min=10000;
    flag=0;
    for(int i=0;i<m;i++)
    {
        if(bb[i]==min) {flag=flag+1;cc[flag]=i;}
        if(bb[i]<min) {min=bb[i];flag=0;cc[flag]=i;}
    }
}
int main()
{
    int m=0;int n=0;int t=0;
    scanf("%d",&t);
    while(t--)
    {
       memset(bb,0,sizeof(bb));
       scanf("%d%d",&m,&n);
       for(int i=0;i<m;i++)
       {
           scanf("%s",aa[i]);printf("%d\n",i);
       }
        oper(m,n);
        int gg=1;
        if(flag==0) {printf("%s",aa[cc[flag]]);}
        if(flag!=0)
        {  for(int j=0;j<n&&gg;j++)
          {
              char miin='T';int minmin=0;
              for(int i=0;i<=flag&&gg;i++)
              {
                      if(aa[cc[i]][j]-'A'<=miin-'A') {miin=aa[cc[i]][j];minmin=cc[i];}
              }
              dd[j]=aa[minmin][j];
              printf("%c",aa[minmin][j]);
          }
        }
        printf("\n");
         if(flag!=0)
         {
          strcpy(aa[m],dd);
          m=m+1;
          oper(m,n);
         }
        printf("%d\n",min);
    }
    return 0;
}

简化算法:

#include <stdio.h>
#include <string.h>
#include <limits.h>
int main()
{   char B[4];int n=0;
    strcpy(B,"ACGT");
    scanf("%d",&n);
    while(n--)
    {int x=0;int y=0;
        scanf("%d",&x);
        scanf("%d",&y);
        char A[x][y+1];
        char C[y+1];memset(C,0,sizeof(C));
        int hamming=0;
        for (int i=0;i<x;i++)
        {scanf("%s",A[i]);}
        for (int j=0;j<y;j++)
        { int flag[4];memset(flag,0,sizeof(flag));int max=INT_MIN;int min=INT_MAX;int d=0;int e=0;
            for (int i=0;i<x;i++)
            {   if (A[i][j]=='A')
            {flag[0]++;}
                if (A[i][j]=='C')
                {flag[1]++;}
                if (A[i][j]=='G')
                {flag[2]++;}
                if (A[i][j]=='T')
                {flag[3]++;}
            }
            
            for (int k=0;k<4;k++)
            {   if (flag[k]>max)
                {max=flag[k];d=k;}
                if (flag[k]<min&&flag[k]>0)
                {min=flag[k];e=k;}
            }
            
            if (min==max)
            {d=e;}
            hamming=hamming+(x-max);
            C[j]=B[d];
            
        }  printf("%s\n",C);
        printf("%d\n",hamming);
        
    }
    return 0;
}

很显然第二个AC了,前一个内存问题太多WA了,改了很多遍还是WA,所以不能算法太复杂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值