You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归最长公共子序列(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;
}

代码关键点解释

  1. 记忆化长度计算:lcs_length函数用len数组存储已经计算过的LCS长度,避免重复递归计算,提升效率。
  2. 多分支递归:在generate_lcs中,当左、上方向的LCS长度相等时,会分别递归两个方向,确保所有可能的LCS都被生成。
  3. 去重处理:每次生成子序列后,都会检查是否已经存在于结果集合中,只有新的子序列才会被存储。
  4. 字符串反转:因为递归是从字符串末尾往前构建子序列的,所以需要反转后才能得到正确的顺序。

运行这段代码,输入jdbac和abjac时,就会输出你预期的bac, jac,不会有重复结果了。

内容的提问来源于stack exchange,提问作者GlonPL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:31:54