递归最长公共子序列(LCS)程序多解输出异常求助
解决递归LCS多解时的重复输出问题
嘿,我完全懂你碰到的这个麻烦——当存在多个长度相同的最长公共子序列(LCS)时,你的递归实现不仅输出了重复结果,还没按预期的顺序展示对吧?比如输入jdbac和abjac时,你想要的是bac, jac,结果却跑出了jac,jac,bac,这确实挺闹心的。
问题根源
你用的way数组本质是在记录单一的遍历路径,但当出现多个可选分支时(比如左、上方向的LCS长度相等,或者当前字符相等但同时存在其他可走的路径),只标记一个方向会导致递归时反复走同一条路生成重复子序列,或者漏掉部分有效分支。
解决方案思路
要解决这个问题,我们需要做两件核心事情:
- 遍历所有可能的有效分支,而不是只走单一路径
- 对生成的子序列做去重处理,避免重复输出
修改后的代码实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #define max 99 // 存储所有唯一的LCS结果 char **lcs_results; int result_count = 0; // 记忆化计算LCS长度 int lcs_length(char *s1, char *s2, int i, int j, int len[max][max]) { if (i == 0 || j == 0) { return len[i][j] = 0; } if (len[i][j] != -1) { return len[i][j]; } if (s1[i-1] == s2[j-1]) { return len[i][j] = 1 + lcs_length(s1, s2, i-1, j-1, len); } else { int left = lcs_length(s1, s2, i, j-1, len); int up = lcs_length(s1, s2, i-1, j, len); return len[i][j] = (left > up) ? left : up; } } // 递归生成所有唯一的LCS void generate_lcs(char *s1, char *s2, int i, int j, int len[max][max], char *current, int pos) { if (i == 0 || j == 0) { // 检查当前子序列是否已存在,避免重复 int exists = 0; for (int k = 0; k < result_count; k++) { if (strcmp(current, lcs_results[k]) == 0) { exists = 1; break; } } if (!exists && pos > 0) { // 反转字符串(递归是从后往前构建的) for (int k = 0; k < pos/2; k++) { char temp = current[k]; current[k] = current[pos-1 -k]; current[pos-1 -k] = temp; } current[pos] = '\0'; // 扩容并存储结果 lcs_results = realloc(lcs_results, (result_count + 1) * sizeof(char*)); lcs_results[result_count] = malloc((pos + 1) * sizeof(char)); strcpy(lcs_results[result_count], current); result_count++; // 反转回去,不影响后续递归 for (int k = 0; k < pos/2; k++) { char temp = current[k]; current[k] = current[pos-1 -k]; current[pos-1 -k] = temp; } } return; } if (s1[i-1] == s2[j-1]) { current[pos] = s1[i-1]; generate_lcs(s1, s2, i-1, j-1, len, current, pos+1); } else { // 左、上方向长度相等时,分别递归两个分支 if (len[i-1][j] == len[i][j]) { generate_lcs(s1, s2, i-1, j, len, current, pos); } if (len[i][j-1] == len[i][j]) { generate_lcs(s1, s2, i, j-1, len, current, pos); } } } int main() { char s1[] = "jdbac"; char s2[] = "abjac"; int len[max][max]; memset(len, -1, sizeof(len)); int lcs_len = lcs_length(s1, s2, strlen(s1), strlen(s2), len); char *current = malloc((lcs_len + 1) * sizeof(char)); lcs_results = NULL; generate_lcs(s1, s2, strlen(s1), strlen(s2), len, current, 0); // 输出结果 printf("所有最长公共子序列:"); for (int i = 0; i < result_count; i++) { if (i > 0) printf(", "); printf("%s", lcs_results[i]); free(lcs_results[i]); } printf("\n"); // 释放内存 free(current); free(lcs_results); return 0; }
代码关键点解释
- 记忆化长度计算:
lcs_length函数用len数组存储已经计算过的LCS长度,避免重复递归计算,提升效率。 - 多分支递归:在
generate_lcs中,当左、上方向的LCS长度相等时,会分别递归两个方向,确保所有可能的LCS都被生成。 - 去重处理:每次生成子序列后,都会检查是否已经存在于结果集合中,只有新的子序列才会被存储。
- 字符串反转:因为递归是从字符串末尾往前构建子序列的,所以需要反转后才能得到正确的顺序。
运行这段代码,输入jdbac和abjac时,就会输出你预期的bac, jac,不会有重复结果了。
内容的提问来源于stack exchange,提问作者GlonPL
相关产品推荐
相关产品推荐

