Codingbat 挑战:sameEnds

Codingbat challenge: sameEnds

提问人:Evgeniy 提问时间:6/1/2022 最后编辑:dani-vtaEvgeniy 更新时间:4/2/2023 访问量:511

问:

给定任务 sameEnds from CodingBat:

给定一个字符串,返回出现在字符串开头和结尾且不重叠的最长子字符串。例如,sameEnds(“abXab”) 是 “ab”。

sameEnds("abXYab") → "ab"
sameEnds("xx") → "x"
sameEnds("xxx") → "x"

我的解决方案通过了除一项之外的所有测试^:

public String sameEnds(String string) {
  String substringFront = "";
  String substringEnd = "";
  String longestSubstring = "";
  
  for (int i = 1; i < string.length() - 1; i++) {
    substringFront = string.substring(0, i);
    substringEnd = string.substring(i);
    
    if (substringEnd.contains(substringFront)) {
      longestSubstring = substringFront;
    }
  }
  
  return longestSubstring;
}

这里有什么问题?我该如何解决?

enter image description here

Java 子字符串 匹配

评论

1赞 Eritrean 6/1/2022
对于长度为 2 的输入,您将不会进入循环,因为 不为 true。将其更改为i < string.length() - 1i <= string.length() - 1

答:

3赞 dani-vta 6/1/2022 #1

代码中存在几个问题:

  • 在到达最后一个字符之前停止迭代,而不是迭代到长度/2 包括(您需要检查开头和结尾而不重叠,因此无需迭代到结尾或几乎)。

  • 你应该从右边开始,所以它应该看起来像.substringEndstring.substring(string.length() - i);

下面是一个固定的实现:

public static void main(String[] args) {
    List<String> listTest = List.of("abXYab", "xx", "xxx", "xxxx", "javaXYZjava", "javajava", "Hello! and Hello!", "x", "", "abcd", "mymmy");
    String substringFront, substringEnd, longestMatch;
    for (String string : listTest) {
        longestMatch = "";
        for (int i = 1; i <= string.length() / 2; i++) {
            substringFront = string.substring(0, i);
            substringEnd = string.substring(string.length() - i);

            if (substringEnd.equals(substringFront) && substringEnd.length() > longestMatch.length()) {
                longestMatch = substringEnd;
            }
        }

        if (!listTest.equals("")) {
            System.out.printf("%s => %s%n", string, longestMatch);
        } else {
            System.out.printf("%s => null%n", string);
        }
    }
}

下面是测试上述代码的链接:

https://www.jdoodle.com/iembed/v0/rEO

但是,为了使其更简洁,您可以使用正则表达式,该正则表达式使用带有贪婪量词的捕获组来匹配字符串的开头,并确保在开始时捕获的内容也与末尾匹配。

^(.+)(.*)\1$

下面是测试正则表达式的链接:

https://regex101.com/r/cgowDQ/1

这可以像这样用 Java 编写:

public static void main(String[] args) {
    List<String> listTest = List.of("abXYab", "xx", "xxx", "xxxx", "javaXYZjava", "javajava", "Hello! and Hello!", "x", "", "abcd", "mymmy");
    Pattern pattern = Pattern.compile("^(.+)(.*)\\1$");
    Matcher matcher;
    for (String s : listTest) {
        matcher = pattern.matcher(s);
        if (matcher.find()) {
            System.out.printf("%s => %s%n", s, matcher.group(1));
        } else {
            System.out.printf("%s => null%n", s);
        }
    }
}

在这里,您可以测试上面的代码:

https://www.jdoodle.com/iembed/v0/rEK

1赞 Mohammad Rifat Arefin 6/1/2022 #2

迭代到 String 中的最后一个元素,更改循环条件可以解决问题。

for (int i = 1; i < string.length(); i++)

评论

2赞 Harshank Bansal 6/1/2022
您也可以迭代到字符串长度的一半。正如问题所说,它不需要重叠的字符。即i <= string.length()/2
5赞 Andreas Violaris 6/1/2022 #3

除了@Dan和其他人恰当地指出的技术问题外,我还想强调不必要的复杂性问题,这在许多情况下会导致这种逻辑错误。

鉴于此,并考虑到这是一个挑战,我更喜欢一种简约的方法,就像下面的方法一样,它是不言自明的,因此易于理解和调试。

public String sameEnds(String string) {
  int middle = string.length() / 2;
  for (int i = middle; i >= 0; i--) {
    String left = string.substring(0, i);
    if (string.endsWith(left)) {
      return left;
    }
  }
  return "";
}
0赞 Evgeniy 4/2/2023 #4

该解决方案通过了所有测试:

public String sameEnds(String string) {
  String substringFront = "";
  String substringEnd = "";
  String longestSubstring = "";
  
  for (int i = 1; i < string.length(); i++) {
    substringFront = string.substring(0, i);
    substringEnd = string.substring(i);
    
    if (substringEnd.contains(substringFront)) {
      longestSubstring = substringFront;
    }
  }
  
  return longestSubstring;
}