最长公共子序列

同样,注意和最长公共子字符串的区别,子序列没有字符连续的要求。问题描述如下:

给定两个字符串str1和str2,求二者的最长公共子序列,一个具体的实例说明是若str1 = “13546”,str2 =“2153467”,那么他们的最长公共子序列为“1346”。

这是算法导论上用来介绍动态规划的时候一个很经典的问题,怎么看有没有最优子结构呢?构建一张表count,count[i][j]表示str1[0,…,i]和str2[0,…,j]的最长子序列,分析如下:

假定现在需求str1[0,…,i]和str2[0,…,j]的最长子序列,那么有以下几种情况:

1.str1[i] ==str2[j] : 此时count[i][j] =count[i-1][j-1]

2.str1[i] != str2[j] : 此时 count[i][j] =max(count[i-1][j],count[i][j-1]

 

一个例子如下:

image

上图中 e表示边界,m表示 往做左上角转,l表示left,u表示up。

 

与之相对应的代码如下:

int lcs_sequence(char *str1,char *str2,int str1_len,int str2_len)
{
        
    //动态规划表
    char *count  = (char *)malloc(str2_len*str1_len*sizeof(char));
    char *record = (char *)malloc(str2_len*str1_len*sizeof(char));
 
    //分别记录当前最长的子序列长度 和 轨迹
 
    for(int h =0;h<str1_len;h++)
    {
        for(int w=0;w<str2_len;w++)
        {
            if(str1[h]==str2[w])
            {
            
                if(h==0 || w==0)
                {
                    count[h*str2_len+w] = 1;
                    record[h*str2_len+w] = 'm';    
                }
                else
                {
                    count[h*str2_len+w] = count[(h-1)*str2_len+w-1]+1;
                    record[h*str2_len+w] = 'm';        
                }
                
            }
            else
            {
                if(h == 0 || w ==0)
                {
                    count[h*str2_len+w] = 0;
                    record[h*str2_len+w] = 'e';    
                }
                else
                {
                    if(count[(h-1)*str2_len+w]>count[h*str2_len+w-1])
                    {
                        count[h*str2_len+w] = count[(h-1)*str2_len+w];
                        record[h*str2_len+w] = 'u';
                    }
                    else
                    {
                        count[h*str2_len+w] = count[h*str2_len+w-1];
                        record[h*str2_len+w] = 'l';
                    }
 
                }
            }
        }
    }
 
//按照轨迹输出
 
    int maxSubSeqLen = count[str1_len*str2_len-1];
    char * result = (char *)malloc((maxSubSeqLen+1)*sizeof(char));
    result[maxSubSeqLen] = '\0';
    int cur = maxSubSeqLen-1;
    int i= str1_len-1;int j =str2_len-1;
 
    while(true)
    {
        if(i<0||j<0)break;
        if(record[i*str2_len+j] == 'm') 
        {
            printf("%c\n",str2[j] );
            result[cur] = str2[j];
            i--;j--;cur--;
        }
        if(record[i*str2_len+j] == 'e')break; 
        if(record[i*str2_len+j] == 'l')
        {
            j--;
        }
        if(record[i*str2_len+j] == 'u')
        {
            i--;
        } 
 
    }
 
 
    printf("\nthe longest common subseq is : %s (length = %d)",result,maxSubSeqLen);
 
    return maxSubSeqLen;
 
}
posted @ 2013-07-29 23:13  曾见绝美的阳光  阅读(215)  评论(0编辑  收藏  举报