【问题标题】:Longest Common Substring: recursive solution?最长公共子串:递归解决方案?
【发布时间】:2014-08-24 03:59:42
【问题描述】:

常见的子串算法:

LCS(x,y) = 1+ LCS(x[0...xi-1],y[0...yj-1] if x[xi]==y[yj]
           else 0

现在动态编程解决方案已经很好理解了。但是我无法弄清楚递归解决方案。如果有多个子串,那么上述算法似乎失败了。

例如:

x = "LABFQDB" and y = "LABDB"

应用上述算法

1+ (x=  "LABFQD" and y = "LABD")
1+ (x=  "LABFQ" and y = "LAB")
return 0 since 'Q'!='B'

返回的值是 2,而我应该是 3?

有人可以指定递归解决方案吗?

【问题讨论】:

    标签: string algorithm recursion dynamic-programming


    【解决方案1】:

    递归+记忆解决方案; 发现所有可能的组合

    class Solution{
        int[][] t;
        int longestCommonSubstr(String s1, String s2, int n, int m){
            // code here
            t = new int[n+1][m+1];
            for(int i = 0; i <=n; i++){
                for(int j = 0; j <=m; j++){
                    t[i][j] = -1;
                }
            }
            for(int i = 0; i<=n; i++){
                t[i][0] = 0;
            }
            for(int j = 0; j<=m; j++){
                t[0][j] = 0;
            }
            solve(s1,s2,n,m);
            // for(int i = 0; i <=n; i++){
            //     for(int j = 0; j <=m; j++){
            //         System.out.print(t[i][j]+" ");
            //     }
            //     System.out.println();
            // }
            int ans = Integer.MIN_VALUE;
            for(int i = 0; i <= n; i++){
                for(int j = 0; j <=m; j++){
                    ans = Math.max(ans,t[i][j]);
                }
            }
            return ans;
        }
        
        private int solve(String s1, String s2, int m, int n){
            if(m == 0 || n == 0) return 0;
            int ans = 0;
            if(s1.charAt(m-1) == s2.charAt(n-1)){
                if(t[m][n] == -1){
                    t[m][n] = 1 + solve(s1,s2,m-1,n-1);
                }
                ans = t[m][n];
            }
            if(t[m-1][n] == -1)
            t[m-1][n] = solve(s1,s2,m-1,n);
            if(t[m][n-1] == -1)
            t[m][n-1] = solve(s1,s2,m,n-1);
            return ans;
    
        }
    }
    

    【讨论】:

      【解决方案2】:

      这是我对最长公共子串问题的递归解决方案。

      ans = 0
      def solve(i,j,s1,s2,n,m,dp):
          global ans
      
          # i is the starting index for s1
          # j is the starting index for s2
      
          if(i >= n or j >= m):
              return 0
      
          if(dp[i][j] != -1):
              return dp[i][j]
          
      
          if(s1[i] == s2[j]):
              dp[i][j] = 1 + solve(i+1,j+1,s1,s2,n,m,dp)
      
          else:
              dp[i][j] = 0
      
          solve(i,j+1,s1,s2,n,m,dp)
          solve(i+1,j,s1,s2,n,m,dp)
          ans = max(ans,dp[i][j]) # ans is storing maximum till now
      
          return dp[i][j]
      
      def longestCommonSubstr(s1, s2, n, m):
          global ans
          # n is the length of s1
          # m is the length s2
          dp = [[-1 for i in range(m)] for j in range(n)]
          ans= 0
          solve(0,0,s1,s2,n,m,dp)
          return ans
      

      【讨论】:

        【解决方案3】:

        希望这可能会有所帮助,即使有很多答案!

         public static int LongestCommonSubString(String x, String y, int m, int n, int curr_max) {
                if (m == 0 || n == 0) return curr_max;
        
                if (x.charAt(m - 1) == y.charAt(n - 1)) return LongestCommonSubString(x, y, m - 1, n - 1, curr_max + 1);
                
                return Math.max(LongestCommonSubString(x, y, m - 1, n, 0), LongestCommonSubString(x, y, m, n - 1, 0));
            }
        

        【讨论】:

        • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
        【解决方案4】:

        下面是计算最长公共字符串的递归方法:

        public int lcsLength(String x, String y)
        {
            char[] xc = x.toCharArray();
            char[] yc = y.toCharArray();
        
            return lcsLength(xc, xc.length - 1, yc, yc.length - 1, 0);
        }
        
        private int lcsLength(char[] xc, int xn, char[] yc, int yn, int currentCsLength)
        {
            if (xn < 0 || yn < 0) {
                return currentCsLength;
            }
            if (xc[xn] == yc[yn]) {
                return lcsLength(xc, xn - 1, yc, yn - 1, currentCsLength + 1);
            }
            else {
                return max(currentCsLength,
                        max(
                                lcsLength(xc, xn - 1, yc, yn, 0),
                                lcsLength(xc, xn, yc, yn - 1, 0)));
            }
        }
        

        使用此解决方案的缺点是它会为 x 和 y 的相同子字符串重新计算多次公共字符串。

        此解决方案使用memoization 技术来避免在递归中多次计算最长公共字符串。

        public int lcsLength(String x, String y)
        {
            char[] xc = x.toCharArray();
            char[] yc = y.toCharArray();
        
            Integer[][] memoization = new Integer[xc.length][yc.length];
        
            return lcsLength(xc, xc.length - 1, yc, yc.length - 1, memoization);
        }
        
        private int lcsLength(char[] xc, int xn, char[] yc, int yn, Integer[][] memoization)
        {
            if (xn < 0 || yn < 0) {
                return 0;
            }
            if (memoization[xn][yn] == null) {
                if (xc[xn] == yc[yn]) {
                    // find out how long this common subsequence is
                    int i = xn - 1, j = yn - 1, length = 1;
                    while (i >= 0 && j >= 0 && xc[i] == yc[j]) {
                        i--;
                        j--;
                        length++;
                    }
                    memoization[xn][yn] = Math.max(length, lcsLength(xc, xn - length, yc, yn - length, memoization));
                }
                else {
                    memoization[xn][yn] = max(
                            lcsLength(xc, xn - 1, yc, yn, memoization),
                            lcsLength(xc, xn, yc, yn - 1, memoization));
                }
            }
        
            return memoization[xn][yn];
        }
        

        【讨论】:

          【解决方案5】:
               int max; //This gloabal variable stores max substring length
               int[][]dp; // 2D Array for Memoization
          
               void main(){
               //--------Main method just intended to demonstrate initialization---------
          
               dp = new int[m+1][n+1] //m and n are string length
               lcs(String a,String b,int n,int m)
               }
               
               //---++++++++-----Recrsive Memoized Function------++++++++++++-------
               static int lcs(String a,String b,int n,int m){
               
               if(dp[n][m]!=-1)return dp[n][m];
              
               if(n==0||m==0)return dp[n][m]=0;
               
               
               if(a.charAt(n-1)==b.charAt(m-1))
               {
                  int res=0;int i=n-1,j=m-1;
                  while((i>=0&&j>=0)&&a.charAt(i)==b.charAt(j)){
                      res++;
                      if(i==0||j==0)return dp[n][m] = Math.max(res,max);
                      i--;j--;
                  }
                  max=Math.max(res,max);
                  
                  return dp[n][m]=Math.max(max,Math.max(lcs(a,b,n-res,m),lcs(a,b,n,m-res)));
                   
               }
               
               return dp[n][m]=Math.max(lcs(a,b,n-1,m),lcs(a,b,n,m-1));
               
               
               
           }
          

          【讨论】:

            【解决方案6】:

            这是 LONGEST COMMON SUBSTRING 的递归代码:

            int LCS(string str1, string str2, int n, int m, int count)
            {
                if (n==0 || m==0)
                    return count;
                if (str1[n-1] == str2[m-1])
                    return LCS(str1, str2, n-1, m-1, count+1);
                return max(count, max(LCS(str1, str2, n-1, m, 0), LCS(str1, str2, n, m-1, 0)));
            }
            

            【讨论】:

              【解决方案7】:

              尽量避免混淆,您要问的是longest common substring,而不是longest common subsequence,它们非常相似,但have differences

              The recursive method for finding longest common substring is:
              Given A and B as two strings, let m as the last index for A, n as the last index for B.
              
                  if A[m] == B[n] increase the result by 1.
                  if A[m] != B[n] :
                    compare with A[m -1] and B[n] or
                    compare with A[m] and B[n -1] 
                  with result reset to 0.
              

              为了更好地说明算法,以下是没有应用记忆的代码。

                 public int lcs(int[] A, int[] B, int m, int n, int res) {
                      if (m == -1 || n == -1) {
                          return res;
                      }
                      if (A[m] == B[n]) {
                          res = lcs(A, B, m - 1, n - 1, res + 1);
                      }
                      return max(res, max(lcs(A, B, m, n - 1, 0), lcs(A, B, m - 1, n, 0)));
                  }
              
                  public int longestCommonSubString(int[] A, int[] B) {
                      return lcs(A, B, A.length - 1, B.length - 1, 0);
                  }
              

              【讨论】:

              • 我们如何添加记忆。
              • 在所有解决方案中,人们从两个字符串的末尾开始?为什么我们不能从字符串的开头开始迭代?
              • @Rupesh 你可以,但基本情况会有所不同。
              • 这个解决方案属于最长公共子序列问题,而不是最长公共子串问题。原始问题也与最长公共子序列有关。
              【解决方案8】:
              import sun.jvm.hotspot.types.CIntegerType;
              
              class RespObject {
                  public int len;
                  public boolean isSubString;
                  int maxLen;
              
                  public RespObject(int len, boolean isSubString, int maxLen) {
                      this.maxLen = maxLen;
                      this.len = len;
                      this.isSubString = isSubString;
                  }
              }
              
              public class LongestCommonSubstring {
                  public static void longestCommonSubstring(String str1, String str2, int i, int j, RespObject resp) {
                      if ((j == str2.length()) || (i == str1.length())) {
                          resp.isSubString = false;
                          resp.len = 0;
                          return;
                      }
                      int currLen = 0;
                      longestCommonSubstring(str1, str2, i + 1, j, resp);
                      RespObject respObject1 = resp;
                      longestCommonSubstring(str1, str2, i, j + 1, resp);
                      RespObject respObject2 = resp;
                      if (str1.charAt(i) == str2.charAt(j)) {
                          longestCommonSubstring(str1, str2, i + 1, j + 1, resp);
                          resp.len += 1;
                          currLen = resp.len;
                          resp.isSubString = true;
                      } else {
                          resp.len = 0;
                          resp.isSubString = false;
                      }
                      resp.maxLen = Integer.max(Integer.max(respObject1.maxLen, respObject2.maxLen), currLen);
                  }
              
                  public static void main(String[] args) {
                      RespObject respObject = new RespObject(0, false, Integer.MIN_VALUE);
                      longestCommonSubstring("dSite:Geeksf", "wSite:GeeksQ", 0, 0, respObject);
                      System.out.println(respObject.len + "   " + String.valueOf(respObject.isSubString) + "  " + respObject.maxLen);
                  }
              }
              

              【讨论】:

              • 您能否进一步描述为什么这个解决方案可以解决问题?
              • 欢迎来到 Stack Overflow。在这里回答问题很好,但是一堆没有解释的代码不是很有用。请edit 回答,包括解释它的作用、工作原理以及它如何回答原始问题。
              • 请解释一下你在做什么。
              【解决方案9】:

              我在 c++ 中为此设计了一个递归解决方案。在我的方法中,我采用特定的 i,j,然后如果它们相等,我将添加 1 并调用 i+1,j+1 的函数,而如果它们不相等,我将在相应的 i 处存储零, j 在我创建的二维数组中。执行后,我正在打印 2D 数组,它似乎没问题。因为我只是填充二维数组,所以我认为时间复杂度必须是 O(mn),其中 m 是一个数组的长度,n 是另一个数组的长度。

              //Finding longestcommonsubword using top down approach [memoization]
              #include<iostream>
              using namespace std;
              
              int findlength(char A[], char B[], int i, int j, int st[][5], int r, int c){
              
              if(r <= i)
                return 0;
              else if(c <= j)
                return 0;
              else{
                  if(A[i] == B[j]){
              
                      if(st[i][j] == -1)
                      st[i][j] = 1+findlength(A, B, i+1, j+1, st, r, c);
                  }else{
                      st[i][j] = 0;
                      int a = findlength(A, B, i, j+1, st, r, c);
                      int b = findlength(A, B, i+1, j, st, r, c);
                  }
              }    
              
              return st[i][j];
              }
              
              int main(){
              int n,m;
              cin>>n>>m;
              char A[n],B[m];
              
              for(int i = 0;i < n;i++)
                cin>>A[i];
              
              for(int j = 0;j < m;j++)
                cin>>B[j];
              
              int st[n][5];
              
              
              for(int k = 0; k < n;k++){
                  for(int l = 0; l< 5;l++){
                     st[k][l] = -1;   
                  }
              }
              findlength(A, B, 0, 0, st, n, 5);
              
              for(int k = 0; k < n;k++){
                  for(int l = 0; l< 5;l++){
                    cout<<st[k][l]<<" ";
                  }
                  cout<<endl;
              }
              
              return 0;
              }
              

              【讨论】:

                【解决方案10】:

                包 algo.dynamic;

                公共类 LongestCommonSubstring {

                public static void main(String[] args) {
                    String a = "LABFQDB";
                    String b = "LABDB";
                    int maxLcs = lcs(a.toCharArray(), b.toCharArray(), a.length(), b.length(), 0);
                    System.out.println(maxLcs);
                }
                
                private static int lcs(char[] a, char[] b, int i, int j, int count) {
                    if (i == 0 || j == 0)
                        return count;
                    if (a[i - 1] == b[j - 1]) {
                        count = lcs(a, b, i - 1, j - 1, count + 1);
                    }
                    count = Math.max(count, Math.max(lcs(a, b, i, j - 1, 0), lcs(a, b, i - 1, j, 0)));
                    return count;
                }
                

                }

                【讨论】:

                  【解决方案11】:
                  long max_sub(int i, int j)
                  {
                  
                      if(i<0 or j<0)
                          return 0;
                  
                      if(s[i]==p[j])
                      {
                          if(dp[i][j]==-1)
                            dp[i][j]=1+max_sub(i-1,j-1);
                      }
                      else
                      {
                          dp[i][j] = 0;
                      }
                      if(i-1>=0 and dp[i-1][j]==-1)
                          max_sub(i-1, j);
                      if(j-1>=0 and dp[i][j-1]==-1)
                          max_sub(i, j-1);
                      return dp[i][j];
                  }
                  

                  您的代码的问题 似乎您没有尝试所有 n^2 种可能性。

                  【讨论】:

                    猜你喜欢
                    • 2018-12-04
                    • 2018-06-23
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-07-23
                    • 2014-04-14
                    • 2020-05-17
                    • 1970-01-01
                    相关资源
                    最近更新 更多