动态编程-最长的公共子字符串:了解空间优化

问题描述

我正在解决一个非常典型的问题,即两个字符串的最长公共子字符串。 问题陈述很清楚: 对于两个字符串s1和s2,找到它们最长的公共子字符串的长度。 我可以理解dp数组代表的状态的定义。它是一个二维数组,其中二维仅代表每个字符串中字符的索引(但仅基于1而不基于0)。 原始的解决方代码如下所示,对我来说似乎很清楚:

  public int findLcslength(String s1,String s2) {
    int[][] dp = new int[s1.length()+1][s2.length()+1];
    int maxLength = 0;
    for(int i=1; i <= s1.length(); i++) {
      for(int j=1; j <= s2.length(); j++) {
        if(s1.charat(i-1) == s2.charat(j-1)) {
          dp[i][j] = 1 + dp[i-1][j-1];
          maxLength = Math.max(maxLength,dp[i][j]);
        }
      }
    }
    return maxLength;
  }

解决方案显然可以优化,因为dp [i] [j]的状态仅取决于前一行,这意味着dp数组足够两行。 因此,我将dp数组设为二维数组,并使用mod操作将索引映射到2范围内。

  static int findLcslength(String s1,String s2) {
    int[][] dp = new int[2][s2.length()+1];
    int maxLength = 0;
    for(int i=1; i <= s1.length(); i++) {
      for(int j=1; j <= s2.length(); j++) {
        if(s1.charat(i-1) == s2.charat(j-1)) {
          dp[i%2][j] = 1 + dp[(i-1)%2][j-1];
          maxLength = Math.max(maxLength,dp[i%2][j]);
        }
      }
    }
    return maxLength;
  }

但是,我的代码并未为所有测试用例提供正确的答案。我找到了一个代码段,可以对所有测试用例给出正确的答案,而我错过了只有一个额外的操作。

  static int findLcslength(String s1,String s2) {
    int[][] dp = new int[2][s2.length()+1];
    int maxLength = 0;
    for(int i=1; i <= s1.length(); i++) {
      for(int j=1; j <= s2.length(); j++) {

        //This is the only extra line I missed
        dp[i%2][j] = 0;

        if(s1.charat(i-1) == s2.charat(j-1)) {
          dp[i%2][j] = 1 + dp[(i-1)%2][j-1];
          maxLength = Math.max(maxLength,dp[i%2][j]);
        }
      }
    }
    return maxLength;
  }

我的代码失败的一种情况是“ passport”和“ ppsspt”,其中我的代码产生了4,但正确的答案显然是3。 我很困惑,但是这行,这行是做什么的,为什么有必要? 希望任何人都可以提供帮助。

解决方法

它将重置当前计数。

您的代码在以下情况下设置此变量:

if(s1.charAt(i-1) == s2.charAt(j-1)) {

但是没有别的设置将其设置为0,这实际上就是该代码的作用。

因此请考虑何时:

s1.charAt(i-1) != s2.charAt(j-1)

在此数组位置中拥有的上一个值将在不应该进行的情况下延续到下一个子字符串比较中。