如何将此递归解决方案转换为记忆的解决方案,以解决最长的常见底带问题?

发布于 2025-02-03 03:17:25 字数 3495 浏览 5 评论 0原文

我能够自己提出递归解决方案,解决了最长的常见子字符串问题:

import static java.lang.Math.max;

public class SolutionRecursive {
    public int longestCommonSubstringLength(String X, String Y) {
        return lcsHelper(X, Y, 1, 1, 0);
    }

    private int lcsHelper(String X, String Y, int xSize, int ySize, int runningSubstringSize) {
        if (xSize > X.length() || ySize > Y.length()) return runningSubstringSize;
        if (X.charAt(xSize - 1) != Y.charAt(ySize - 1)) {
            return max(runningSubstringSize,
                    max(lcsHelper(X, Y, xSize + 1, ySize, 0),
                            lcsHelper(X, Y, xSize, ySize + 1, 0)));
        }
        return lcsHelper(X, Y, xSize + 1, ySize + 1, runningSubstringSize + 1);
    }
}

我对其进行了测试,而且似乎很好,但是我很难记住递归电话。


import java.util.Arrays;

public class SolutionMemoized implements SolutionLCS{
    private final int UNFILLED = Integer.MIN_VALUE;

    public int longestCommonSubstringLength(String X, String Y) {
        int[][][] memo = new int[X.length() + 1][Y.length() + 1][X.length() + 1];
        for (int i = 1; i < X.length() + 1; i++) {
            for (int j = 0; j < Y.length() + 1; j++) {
                Arrays.fill(memo[i][j], UNFILLED);
            }
        }
        int lcs = lcsHelper(X, Y, X.length(), Y.length(), memo, 0);
        return lcs;
    }

    private int lcsHelper(String X, String Y, int xSize, int ySize, int[][][] memo, int runningSubstringSize) {
        if (xSize == 0 || ySize == 0) {
            return runningSubstringSize;
        }
        if (memo[xSize][ySize][runningSubstringSize] != UNFILLED) {
            return memo[xSize][ySize][runningSubstringSize];
        }
        if (X.charAt(xSize - 1) == Y.charAt(ySize - 1)) {
            memo[xSize][ySize][runningSubstringSize] = lcsHelper(X, Y, xSize - 1, ySize - 1, memo,
                    runningSubstringSize + 1);
        } else {
            memo[xSize][ySize][runningSubstringSize] = Math.max(runningSubstringSize,
                    Math.max(
                            lcsHelper(X, Y, xSize - 1, ySize, memo, 0),
                            lcsHelper(X, Y, xSize, ySize - 1, memo, 0)
                    ));
        }
        return memo[xSize][ySize][runningSubstringSize];
    }
}

This apparently fails for longestCommonSubstringLength("KXCGMTMVVGFQQWSPD","JXZADDUKVLQPKUZJZHWSUTPCAFSYAIBJHAMMEGWBTPQELRNKBLDXGUZGCSEC") for which the required answer is 2 but it gives 1. I couldn't find lower length failed tests.

谁能告诉我为什么不起作用?我觉得我的递归和回忆代码之间有清晰的1-1对应关系。

on youtube 我找到了一个修复程序,但作者没有解释他到达了。该修复程序将我的最后几行代码行更改为:

  int s1 = runningSubstringSize;

        if (X.charAt(xSize - 1) == Y.charAt(ySize - 1)) {
            s1 = lcsHelper(X, Y, xSize - 1, ySize - 1, memo,
                    runningSubstringSize + 1);
        }
        int s2 = Math.max(runningSubstringSize,
                Math.max(
                        lcsHelper(X, Y, xSize - 1, ySize, memo, 0),
                        lcsHelper(X, Y, xSize, ySize - 1, memo, 0)
                ));

        return memo[xSize][ySize][runningSubstringSize] = Math.max(s1, s2);

为什么我的解决方案(代码块2)即使它与递归代码之间似乎有1-1对应关系?修复程序如何纠正它?

I was able to come up with the recursive solution to the longest common substring problem on my own:

import static java.lang.Math.max;

public class SolutionRecursive {
    public int longestCommonSubstringLength(String X, String Y) {
        return lcsHelper(X, Y, 1, 1, 0);
    }

    private int lcsHelper(String X, String Y, int xSize, int ySize, int runningSubstringSize) {
        if (xSize > X.length() || ySize > Y.length()) return runningSubstringSize;
        if (X.charAt(xSize - 1) != Y.charAt(ySize - 1)) {
            return max(runningSubstringSize,
                    max(lcsHelper(X, Y, xSize + 1, ySize, 0),
                            lcsHelper(X, Y, xSize, ySize + 1, 0)));
        }
        return lcsHelper(X, Y, xSize + 1, ySize + 1, runningSubstringSize + 1);
    }
}

I tested it and it seems to work fine but I am having trouble memoizing the recursive calls.


import java.util.Arrays;

public class SolutionMemoized implements SolutionLCS{
    private final int UNFILLED = Integer.MIN_VALUE;

    public int longestCommonSubstringLength(String X, String Y) {
        int[][][] memo = new int[X.length() + 1][Y.length() + 1][X.length() + 1];
        for (int i = 1; i < X.length() + 1; i++) {
            for (int j = 0; j < Y.length() + 1; j++) {
                Arrays.fill(memo[i][j], UNFILLED);
            }
        }
        int lcs = lcsHelper(X, Y, X.length(), Y.length(), memo, 0);
        return lcs;
    }

    private int lcsHelper(String X, String Y, int xSize, int ySize, int[][][] memo, int runningSubstringSize) {
        if (xSize == 0 || ySize == 0) {
            return runningSubstringSize;
        }
        if (memo[xSize][ySize][runningSubstringSize] != UNFILLED) {
            return memo[xSize][ySize][runningSubstringSize];
        }
        if (X.charAt(xSize - 1) == Y.charAt(ySize - 1)) {
            memo[xSize][ySize][runningSubstringSize] = lcsHelper(X, Y, xSize - 1, ySize - 1, memo,
                    runningSubstringSize + 1);
        } else {
            memo[xSize][ySize][runningSubstringSize] = Math.max(runningSubstringSize,
                    Math.max(
                            lcsHelper(X, Y, xSize - 1, ySize, memo, 0),
                            lcsHelper(X, Y, xSize, ySize - 1, memo, 0)
                    ));
        }
        return memo[xSize][ySize][runningSubstringSize];
    }
}

This apparently fails for longestCommonSubstringLength("KXCGMTMVVGFQQWSPD","JXZADDUKVLQPKUZJZHWSUTPCAFSYAIBJHAMMEGWBTPQELRNKBLDXGUZGCSEC") for which the required answer is 2 but it gives 1. I couldn't find lower length failed tests.

Could anyone tell me why it doesn't work? I feel like there is clear 1-1 correspondence between my recursive and memoized codes.

On youtube I found a fix but the author didn't explain how he arrived at it. The fix is changing my last few lines of code to:

  int s1 = runningSubstringSize;

        if (X.charAt(xSize - 1) == Y.charAt(ySize - 1)) {
            s1 = lcsHelper(X, Y, xSize - 1, ySize - 1, memo,
                    runningSubstringSize + 1);
        }
        int s2 = Math.max(runningSubstringSize,
                Math.max(
                        lcsHelper(X, Y, xSize - 1, ySize, memo, 0),
                        lcsHelper(X, Y, xSize, ySize - 1, memo, 0)
                ));

        return memo[xSize][ySize][runningSubstringSize] = Math.max(s1, s2);

Why doesn't my solution (code block 2) work even though there seems to be 1-1 correspondence between it and the recursive code? And how is the fix correcting it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

我喜欢麦丽素 2025-02-10 03:17:25

您添加的修复程序已经使问题很明显:

如果当前字符相等,则贪婪地增加了runningsubstringsize ,并为较小的字符串重复。示例:<代码>(ABDC,AFC)的解决方案简化为(ABD,AF) + 1 的解决方案。

但是,您并不考虑仅减少一个字符串并检查它是否提供更好的解决方案的“正常”方案/流动。从上一个示例中,理想情况下应该是

solution(ABDC, AFC) = max (
    solution(ABD, AFC),
    solution(ABDC, AF),
    solution(ABD, AF) + 1
)

添加的修复程序。在设置回忆表中的更新值之前,它会比较所有方案。

The fix you've added has made the issue evident:

If the current characters are equal, you greedily increase the runningSubstringSize and recurse for the smaller strings. Example: solution for (ABDC, AFC) reduced to solution for (ABD, AF) + 1.

However, you're not considering the "normal" scenario/flow of reducing only one string and checking if it gives a better solution. From the previous example, it should ideally be

solution(ABDC, AFC) = max (
    solution(ABD, AFC),
    solution(ABDC, AF),
    solution(ABD, AF) + 1
)

The added fix does exactly that; it compares all scenarios before setting the updated value in the memoization table.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文