最长公共子序列
同样,注意和最长公共子字符串的区别,子序列没有字符连续的要求。问题描述如下:
给定两个字符串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]
一个例子如下:
上图中 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;
} |