对于交错字符串的算法进行进一步优化。

huangapple go评论60阅读模式
英文:

Further optimization to the algo for interleaving string

问题

我正在尝试解决LeetCode问题97. 交错字符串

给定三个字符串s1s2s3,编写一个程序来检查s3是否是s1s2的交错。

问题说明:

如果s3包含s1s2的所有字符,并且各个字符串中字符的顺序保持不变,则称s3s1s2的交错。

示例 1:

  • 输入: S1 = "xxyzz",S2 = "wyyzx",S3 = "xxwyyzyzxz"
  • 输出: true
  • 说明: "xx"(来自S1) + "wyyz"(来自S2) + "yz"(来自S1) + "x"(来自S2) + "z"(来自S1)

示例 2:

  • 输入: S1 = "xxyzz",S2 = "wyyzx",S3 = "xxwyyyxzzz"
  • 输出: false
  • 说明: 无法通过交错S1和S2来得到S3。

我使用了递归和记忆化来解决它。递归重新计算子问题,因此我使用了记忆化。它通过了所有测试用例,但在最后一个测试用例上仍然超出了时间限制。我相信我的代码中有一个子串比较片段仍然没有优化,可以使代码运行得更快。任何建议都将有助于我。

以下是使用递归的代码(我需要帮助优化使用记忆化的代码):

public static boolean interleave(String s1, String s2, String s3,
                                int i1, int i2, int i3) {
    if(i1<0) {
        return s2.substring(0,i2+1).equals(s3.substring(0,i3+1));
    }

    if(i2<0) {
        return s1.substring(0,i1+1).equals(s3.substring(0,i3+1));
    }

    boolean s1path = s1.charAt(i1) == s3.charAt(i3) &&
            interleave(s1,s2,s3,i1-1,i2,i3-1);
    boolean s2path = s2.charAt(i2) == s3.charAt(i3) &&
            interleave(s1,s2,s3,i1,i2-1,i3-1);
    return s1path || s2path;
}

以下是需要进行一些优化的使用记忆化的代码:

public static boolean interleavememo(String s1, String s2, String s3,
                                    int i1, int i2, int i3, int[][]dp) {
    if(i1<0) {
        var r = s2.substring(0,i2+1).equals(s3.substring(0,i3+1));
        return r;
    }

    if(i2<0) {
        var r = s1.substring(0,i1+1).equals(s3.substring(0,i3+1));
        return r;
    }

    var val = dp[i1][i2];
    if(val == 1) return true;
    if(val == 0) return false;

    boolean s1path = s1.charAt(i1) == s3.charAt(i3) &&
            interleavememo(s1,s2,s3,i1-1,i2,i3-1, dp);
    boolean s2path = s2.charAt(i2) == s3.charAt(i3) &&
            interleavememo(s1,s2,s3,i1,i2-1,i3-1,dp);
    boolean result = s1path||s2path;
    if(result) {
        dp[i1][i2] = 1;
    }
    return result;
}

示例输入:

public static void main(String[] args) {
    var s1 = "ABC";
    var s2 = "ACD";
    var s3 = "ACDABC";
    var i1 = s1.length()-1;
    var i2 = s2.length()-1;
    var i3 = s3.length()-1;
    var dp = new int[s1.length()][s2.length()];
    for(int i = 0; i<dp.length; i++) {
        Arrays.fill(dp[i],-1);
    }
    var retval = interleavememo(s1,s2,s3, i1, i2, i3, dp);

    System.out.println(retval);
}
英文:

I am trying to solve LeetCode problem 97. Interleaving String
:

> Given three strings s1, s2 and s3, write a program that checks whether s3 is an interleaving of s1 and s2.

Problem Note:

S3 is said to be interleaving S1 and S2 if it contains all characters of S1 and S2 and the order of all characters in individual strings is preserved.

Example 1:

  • Input: S1 = "xxyzz", S2 = "wyyzx" and S3 = "xxwyyzyzxz"
  • Output: true
  • Explanation: "xx" (from S1) + "wyyz" (from S2) + "yz" (from S1) + "x" (from S2) + "z" (from S1)

Example 2:

  • Input: S1 = "xxyzz", S2 = "wyyzx", S3 = "xxwyyyxzzz"
  • Output: false
  • Explanation: It is not possible to get S3 by interleaving S1 and S2.

I solved it with recursion and memoization. The recursion re-computing subproblems is slower, so I submitted my solution with memoization. It passes all test cases and right at the last test case -- which does pass as per the website -- I still get time limit exceeded. I believe there is a substring comparison snippet in my code that I am still not optimizing that can make the code run faster. Any suggestions will be helpful.

Here is the code with recursion (I am looking for help on the one with memoization)

     public static boolean interleave(String s1, String s2, String s3,
                                     int i1, int i2, int i3) {
        if(i1&lt;0) {
            return s2.substring(0,i2+1).equals(s3.substring(0,i3+1));
        }

        if(i2&lt;0) {
           return s1.substring(0,i1+1).equals(s3.substring(0,i3+1));
        }

        boolean s1path = s1.charAt(i1) == s3.charAt(i3) &amp;&amp;
                interleave(s1,s2,s3,i1-1,i2,i3-1);
        boolean s2path = s2.charAt(i2) == s3.charAt(i3) &amp;&amp;
                interleave(s1,s2,s3,i1,i2-1,i3-1);
        return s1path || s2path;
    }

Here is the code with memoization that requires a bit of optimization that I am unable to do

    public static boolean interleavememo(String s1, String s2, String s3,
                                     int i1, int i2, int i3, int[][]dp) {
        if(i1&lt;0) {
            var r = s2.substring(0,i2+1).equals(s3.substring(0,i3+1));
            return r;
        }

        if(i2&lt;0) {
            var r = s1.substring(0,i1+1).equals(s3.substring(0,i3+1));
            return r;
        }

        var val = dp[i1][i2];
        if(val == 1) return true;
        if(val == 0) return false;

        boolean s1path = s1.charAt(i1) == s3.charAt(i3) &amp;&amp;
                interleavememo(s1,s2,s3,i1-1,i2,i3-1, dp);
        boolean s2path = s2.charAt(i2) == s3.charAt(i3) &amp;&amp;
                interleavememo(s1,s2,s3,i1,i2-1,i3-1,dp);
        boolean result = s1path||s2path;
        if(result) {
            dp[i1][i2] = 1;
        }
        return result;
    }

Example input

    public static void main(String[] args) {
        var s1 = &quot;ABC&quot;;
        var s2 = &quot;ACD&quot;;
        var s3 = &quot;ACDABC&quot;;
        var i1 = s1.length()-1;
        var i2 = s2.length()-1;
        var i3 = s3.length()-1;
        var dp = new int[s1.length()][s2.length()];
        for(int i = 0; i&lt;dp.length; i++) {
            Arrays.fill(dp[i],-1);
        }
        var retval = interleavememo(s1,s2,s3, i1, i2, i3, dp);

        System.out.println(retval);
    }

答案1

得分: 1

你的记忆化代码只缺少一些东西:

  • 我没有看到任何检查 s3 的大小是否等于另外两个字符串大小之和的代码。如果这个基本条件不成立,你应该立即返回 false。

  • 记忆化应该在没有成功的情况下也发生。没有代码来存储值 0。记忆化失败和记忆化成功一样有价值。

  • 如果第一个递归调用成功,就不应该进行第二个递归调用。你可以使用短路求值的原理来处理这个问题,将两个表达式放在一个表达式中。

下面是已更正的代码:

    public boolean isInterleave(String s1, String s2, String s3) {
        var i1 = s1.length();
        var i2 = s2.length();
        var i3 = s3.length();
        // 确保基于长度是有可能的:
        if (i1 + i2 != i3) return false; // 快速退出
        var dp = new int[i1][i2];
        for(int i = 0; i < dp.length; i++) {
            Arrays.fill(dp[i], -1);
        }
        var retval = interleavememo(s1, s2, s3, i1 - 1, i2 - 1, i3 - 1, dp);
        return retval;
    }

    public static boolean interleavememo(String s1, String s2, String s3,
                                         int i1, int i2, int i3, int[][]dp) {
        if (i1 < 0) {
            var r = s2.substring(0, i2 + 1).equals(s3.substring(0, i3 + 1));
            return r;
        }
        if (i2 < 0) {
            var r = s1.substring(0, i1 + 1).equals(s3.substring(0, i3 + 1));
            return r;
        }
        var val = dp[i1][i2];
        if (val != -1) return val == 1; // 将两种情况结合在一个退出点。

        // 使用短路求值
        boolean result = s1.charAt(i1) == s3.charAt(i3) &&
                    interleavememo(s1, s2, s3, i1 - 1, i2, i3 - 1, dp)
                || s2.charAt(i2) == s3.charAt(i3) &&
                    interleavememo(s1, s2, s3, i1, i2 - 1, i3 - 1, dp);
        // 无论结果如何,都要存储它;不仅在成功时
        dp[i1][i2] = result ? 1 : 0;
        return result;
    }
英文:

Your memoization code only lacks a few things:

  • I didn't see any code that checks whether the size of s3 is the sum of the sizes of the other two strings. If this basic condition is not true, you should immediately return false.

  • The memoization should happen also when you don't have success. There is no code where you store the value 0. Memoization of failure is just as valuable as the memoization of success.

  • If you have success with the first recursive call, you shouldn't make the second recursive call. You can use the principle of short-cut evaluation for this and put both expressions in one expression.

Here is the corrected code:

    public boolean isInterleave(String s1, String s2, String s3) {
        var i1 = s1.length();
        var i2 = s2.length();
        var i3 = s3.length();
        // Make sure it is even possible, based on lengths:
        if (i1 + i2 != i3) return false; // Quick exit
        var dp = new int[i1][i2];
        for(int i = 0; i &lt; dp.length; i++) {
            Arrays.fill(dp[i], -1);
        }
        var retval = interleavememo(s1, s2, s3, i1 - 1, i2 - 1, i3 - 1, dp);
        return retval;
    }

    public static boolean interleavememo(String s1, String s2, String s3,
                                         int i1, int i2, int i3, int[][]dp) {
        if (i1 &lt; 0) {
            var r = s2.substring(0, i2 + 1).equals(s3.substring(0, i3 + 1));
            return r;
        }
        if (i2 &lt; 0) {
            var r = s1.substring(0, i1 + 1).equals(s3.substring(0, i3 + 1));
            return r;
        }
        var val = dp[i1][i2];
        if (val != -1) return val == 1; // combine both cases in one exit point.

        // Use shortcut evaluation
        boolean result = s1.charAt(i1) == s3.charAt(i3) &amp;&amp;
                    interleavememo(s1, s2, s3, i1 - 1, i2, i3 - 1, dp)
                || s2.charAt(i2) == s3.charAt(i3) &amp;&amp;
                    interleavememo(s1, s2, s3, i1, i2 - 1, i3 - 1,dp);
        // Whatever the result, store it; not only when success
        dp[i1][i2] = result ? 1 : 0;
        return result;
    }

huangapple
  • 本文由 发表于 2023年5月28日 02:03:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76348325.html
  • algorithm
  • data-structures
  • dynamic-programming
  • memoization
  • recursion
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定