【问题标题】:Recursive isSubtring method java递归isSubstring方法java
【发布时间】:2014-03-04 03:47:00
【问题描述】:

我有以下问题:

用下面的签名写一个递归静态方法isSubstring -

public static boolean isSubstring(String s1,String s2)

获取两个字符串 - s1、s2,如果 s2 是 s1 的 子字符串,则返回 true。

该方法应该是递归的,根本不使用迭代。还有你写的任何其他方法(如果你写的话)。

正确答案不会改变方法类型签名/注解(甚至不会通过重载)

您只能在解决方案中使用以下方法:

public char charAt(int i)

公共整数长度()

公共字符串子字符串(int i)


这就是我目前所拥有的,我知道它不起作用 IE isSubstring("hello","ho") 将返回 true。知道可以做什么吗?

public static boolean isSubstring(String s1, String s2) {
    if (s2.length() == 0)
        return true;
    if ((s1.length() == 0) || (s1.length() < s2.length()))
        return false;
    if (s1.charAt(0) != s2.charAt(0))
        return isSubstring(s1.substring(1), s2);
    else
        return isSubstring(s1.substring(1), s2.substring(1));
}

【问题讨论】:

  • 最简单的方法是在找到第一个字符后,使用另一种递归方法来验证 s2 的其余部分是否匹配。另一个有趣的测试用例是isSubstring("hello","lo"),它应该返回true

标签: java string recursion methods


【解决方案1】:
public static boolean isSubstring(String s1, String s2){
    if (s2.length == 0) return true;
    if (s1.length < s2.length) return false;
    if (s1.charAt(0) == s2.charAt(0)) 
        return isPrefixString(s1.substring(1, s2.length), s2.substring(1, s2.length)) 
            || isSubstring(s1.substring(1, s1.length), s2);
    return isSubstring(s1.substring(1, s1.length), s2);
}

private static boolean isPrefixString(String s1, String s2){
    if (s2.length == 0) return true;
    if (s1.charAt(0) != s2.charAt(0)) return false;
    return isPrefixString(s1.substring(1, s1.length), s2.substring(1, s2.length));
}

基本算法:

isSubstring 函数:

基本情况 1:如果 s2 为空(因为它是空的,或者因为它已经用尽了),则返回 vacuous truth(每个字符串都包含空字符串)。

基本情况 2:如果 s1 比 s2 短,则 s1 不可能包含 s2,因此返回重言式 false。

递归情况 1:如果 s1 包含 s2,则 s1 的某些子字符串以与 s2 开头的相同字母开头。因此,如果 s1 不是以与 s2 相同的字母开头,请在字符串中找到其他点作为起点。通过“其他”,该算法只是线性迭代字符串。

递归情况2:如果s1和s2以相同的字符串开头,递归测试s2是否是s1的前缀字符串(即s1是否以s2开头)。如果 s2 不是 s1 的前缀字符串,则尝试另一个起点。否则,s2 是 s1 的前缀,因此 s1 包含 s2。

请注意,与我在此处编写的方式相比,代码具有递归案例 1 和 2。

isPrefixString 函数:

基本情况 1:如果 s2 为空,则返回(空)真值。

基本情况 2:如果 s1 和 s2 不是以相同的字母开头,那么 s2 不可能是 s1 的前缀,所以返回 false。

递归情况:如果 s1 和 s2 以相同的字符开头,则检查下一个字符是否也相同。

【讨论】:

    【解决方案2】:

    递归应该被认为是两个部分。

    1) 基本情况。这是很容易确定的微不足道的情况。 到目前为止,您所拥有的是基本案例的良好开端。 1) 字符串是否小于 3?然后返回假。 2)字符串是否以“bab”开头然后返回true。

    2) 递归情况。 这会将问题拆分为更小的问题,如果您将它们拆分得足够多,希望您有一个基本案例。

    这是一个简单递归的好例子。

    public static boolean substringRec(String str, String str2) {
          if (str.length() < str2.length()) return false;
          if (str.length() == str2.length())
          {
              if (equals(str, str2))
                return true;
              return false;
          }
          else
            return substringRec(str.substring(1), str2);    
    }
    
    public static boolean equals(String str1, String str2)
    {
        if (str1.charAt(0) == str2.charAt(0))
        {
            if (str1.length() == 1)
                return true;
    
            return equals(str1.substring(1), str2.substring(1));                
        }
        return false;
    }
    

    这使用简单的基本情况,并作为递归情况检查从索引一开始的字符串。这仅将我们的字符串减少了一个章程,但足以解决问题。我必须实现一个 .equals ,因为显然你不能使用它,你可以用类似的方式来做。

    这意味着我们最终得到了字符串长度的堆栈级别。

    我们可以做得更好吗?稍微,通过将问题分成两半,我们只需要 ln(n) 的堆栈级别,其中 n 是字符串的长度。对于大多数长度来说,这并不是什么大问题,但如果您正在搜索长度为一百万的字符串,它可能很重要。在第一个版本中,我们的堆栈深度约为 1,000,000!但有了这个二进制版本,我们只需要深入 14 层即可。

    虽然这是有代价的,但我们的解决方案涉及更多,并且会打破您在作业中的一些规则,但我认为这很有趣。

    我们的基本案例 1) 如果字符串小于搜索字符串的长度,则返回 false。 2)如果字符串是搜索字符串的长度,如果相等则返回true,否则返回false。 3) 如果字符串出现在字符串的中点之上,则返回 true。

    如果这些都不正确,我们将字符串分成两部分并递归检查该字符串。

    这是该算法的一个示例,尽管它尚未经过全面测试,但我很确定它会没问题。

    public static boolean substringRec2(String str, String searchString) {       
    
          // Base cases
          if (str.length() < searchString.length()) 
              return false;
    
          if (str.length() == searchString.length())
          {
              if (str.equals(searchString))
              {
                  return true;
              }
              return false;
          }
    
          int halfWay = str.length()/2;
    
          // Now check for the search string over the "break"
          for (int i = 0; i < searchString.length(); i++)
          {
              int startIndex = halfWay - 1 - i;
              int endIndex = startIndex + 3;
              if (startIndex >= 0)
              {
                  String substring = str.substring(startIndex, endIndex);
                  if (substring.equals(searchString))
                  {
                      return true;
                  }
              }
          }
    
         // Recursive Cases 
         //  We did find the search string over the break,so break the string into two equal(ish) pieces and check those 
         if(substringRec2(str.substring(0,halfWay -1), searchString))
             return true;
    
         if(substringRec2(str.substring(halfWay, str.length()), searchString))
             return true;
    
         return false;
    }
    

    注意 - 取自我之前的一个答案并修改 - https://stackoverflow.com/a/21594554/3096507

    【讨论】:

    • OP 说:该方法应该是递归的,根本不使用迭代
    • 另外you can only use the following methods in your solution:不包括equals()方法。
    • @MT0 - 感谢更新我的第一个示例以删除 equals(),或者仅使用允许的函数给出递归定义。我知道第二个示例不符合限制条件,但是对于学习递归的人来说很有趣,所以我将其包括在内。
    【解决方案3】:
    public static boolean isSubstring(String s1, String s2) {
        return s2.length() == 0
                || ( s1.length() >= s2.length()
                    && ( matchStartOfString( s1, s2 )
                        || isSubstring( s1.substring(1), s2 )
                        )
                );
    }
    
    private static boolean matchStartOfString( final String s1, final String s2 )
    {
        return s2.length() == 0
                || (    s1.length() >= s2.length()
                    &&  s1.charAt( 0 ) == s2.charAt( 0 )
                    &&  matchStartOfString( s1.substring(1), s2.substring(1) )
                );
    }
    

    【讨论】:

      【解决方案4】:

      您的解决方案几乎很好,但您必须以某种方式记住您进行了替换。我提出以下解决方案(使用具有不同原型的辅助函数,符合问题的要求):

      public static boolean sub(final String s1, final String s2, final boolean hasReplaced) {
          if (s2.length() == 0) {
              return true;
          }
          if ((s1.length() == 0) || (s1.length() < s2.length())) {
              return false;
          }
          if (s1.charAt(0) != s2.charAt(0)) {
              if (hasReplaced) {
                  return false;
              }
              return sub(s1.substring(1), s2, hasReplaced);
          }
          return sub(s1.substring(1), s2.substring(1), true);
      }
      
      public static boolean isSubstring(final String s1, final String s2) {
          return sub(s1, s2, false);
      }
      

      【讨论】:

      • 不幸的是,我相信你只是编码了某种startsWith。我认为这只会返回true 如果s1 开始s2
      • 其实isSubstring("I say hello", "hello"));返回true。不过肯定还是有bug,因为isSubstring("hello", "lo")返回false
      【解决方案5】:

      我认为应该这样做。

      它使用了一种辅助方法。当您看到第一个匹配项时,它将调用辅助方法并验证子字符串是否从该点匹配。如果没有,它会在下一场比赛中尝试同样的方法。

      public static boolean isSubstring(final String s1, final String s2) {
          if (s2.length() == 0) {
              return true;
          }
      
          if ((s1.length() == 0) || (s1.length() < s2.length())) {
              return false;
          }
      
          if (s1.charAt(0) != s2.charAt(0)) {
              return isSubstring(s1.substring(1), s2);
          }
      
          if (!isSubstringAux(s1.substring(1), s2.substring(1))) {
              return isSubstring(s1.substring(1), s2);
          }
      
          return true;
      }
      
      public static boolean isSubstringAux(final String s1, final String s2) {
          if (s2.length() == 0) {
              return true;
          }
      
          if (s1.charAt(0) == s2.charAt(0)) {
              return isSubstringAux(s1.substring(1), s2.substring(1));
          }
      
          return false;
      }
      

      【讨论】:

        猜你喜欢
        • 2012-10-28
        • 2012-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-02
        相关资源
        最近更新 更多