Codingbat 挑战:使用 Stream API 的 mirrorEnds

Codingbat challenge: mirrorEnds using Stream API

提问人:Evgeniy 提问时间:6/6/2022 最后编辑:Alexander IvanchenkoEvgeniy 更新时间:6/7/2022 访问量:154

问:

给定 CodingBat 的任务 mirrorEnds

给定一个字符串,在两个字符串中查找镜像(向后)字符串 给定字符串的开头和结尾。

换言之,零或 在给定字符串的开头有更多字符,并且在 字符串的末尾以相反的顺序排列(可能重叠)。为 例如,字符串的 Mirror End 是 。"abXYZba""ab"

例子:

mirrorEnds("abXYZba")  →  "ab"
mirrorEnds("abca")     →  "a"
mirrorEnds("aba")      →  "aba"

我对此任务的解决方案如下:

public String mirrorEnds(String str) {
  String result = "";
  
  if (str.length() % 2 != 0) {
    for (int i = 0; i < str.length() / 2; i++) {
      if (str.charAt(i) == str.charAt(str.length() - i - 1)) {
        result += "" + str.charAt(i);
      } else {
        break;
      }
    }
    
    if (result.length() == str.length() / 2) {
      String strEnd = new StringBuilder(result).reverse().toString();
      result += "" + str.charAt(str.length() / 2) + strEnd;
    } 
  }
  
  if (str.length() % 2 == 0) {
    for (int i = 0; i < str.length() / 2; i++) {
      if (str.charAt(i) == str.charAt(str.length() - i - 1)) {
        result += "" + str.charAt(i);
      } else {
        break;
      }
    }
    
    if (result.length() == str.length() / 2) {
      String strEnd = new StringBuilder(result).reverse().toString();
      result += strEnd;
    }
  }
  
  return result;
}

是否可以使用 Stream API 解决这个问题?

字符串 java-stream

评论

1赞 Kayaman 6/6/2022
是的,是的。

答:

2赞 HariHaravelan 6/6/2022 #1

我认为 Stream API 不会给你带来任何优势。但是,您可以像这样优化代码

  public String mirrorEnds(String string) {
    StringBuilder result = new StringBuilder();

    for (int i = 0; i < string.length(); i++) {
        if (string.charAt(i) == string.charAt(string.length() - i - 1)) {
            result.append(string.charAt(i));
        } else {
            break;
        }
    }
    return result.toString();
}

评论

0赞 Holger 6/7/2022
您可以进一步优化它:for(int i = 0; i < string.length(); i++) { if(string.charAt(i) != string.charAt(string.length() - i - 1)) return string.substring(0, i); } return string;
1赞 dani-vta 6/6/2022 #2

由于您特意要求此问题的流解决方案,因此这可能是寻找第一个字符与最后一个反转的字符匹配的最大索引,从开始并通过递减继续,直到达到 0。iiiilength() / 2i

仅当给定的字符串不是回文时,才应用以前的算法。事实上,在这种情况下,字符串本身可以立即返回。

public static String mirrorEnds(String str) {
    if (str.equals(new StringBuilder(str).reverse().toString())) {
        return str;
    }

    OptionalInt maxLen = IntStream.iterate(str.length() / 2, i -> i >= 0, i -> i - 1)
            .filter(i -> str.substring(0, i).equals((new StringBuilder(str.substring(str.length() - i))).reverse().toString()))
            .max();

    return maxLen.isPresent() ? str.substring(0, maxLen.getAsInt()) : null;
}

输出

ab
a
aba

下面是测试代码的链接:

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

2赞 Alexander Ivanchenko 6/6/2022 #3

它可以在单个流语句中实现。IntStream.range()

为了创建一个单行代码,我们需要 的帮助,这是在 Java 9 中引入的。 是一种所谓的短 cercuit 操作,即它将在与给定谓词不匹配的第一个元素之后中断。takeWhile()takeWhile()

public static String mirrorEnds(String str) {
    
    return IntStream.range(0, str.length())
        .takeWhile(i -> str.charAt(i) == str.charAt(str.length() - 1 - i))
        .map(str::codePointAt)
        .collect(StringBuilder::new,
            StringBuilder::appendCodePoint,
            StringBuilder::append)
        .toString();
}

由于 CodingBat 仍在 Java 8 上,它的编译器会抱怨上面的代码。


@Holger 提出的一个非常好的、符合 Java 8 的简单解决方案。

它查找开头和结尾字符之间第一个不匹配的索引,并生成一个从开头到索引的子字符串。0mismatch

如果返回一个空的可选字符串,即给定的字符串是回文并且没有不匹配,则给定字符串的长度将通过 提供。findFirst()orElse()

下面的代码通过了 CodingBat 上的所有测试。

public String mirrorEnds(String str) {
        
        int mismatch = IntStream.range(0, str.length() / 2)
            .filter(i -> str.charAt(i) != str.charAt(str.length() - 1 - i))
            .findFirst()
            .orElse(str.length());
        
        return str.substring(0, mismatch);
    }

注意,由于 CodingBat 不允许导入,为了在本站点上运行上述代码,您需要使用所谓的类的完全限定名称。java.util.stream.IntStream

评论

2赞 Holger 6/7/2022
更短且兼容 Java 8:return str.substring(0, IntStream.range(0, str.length()) .filter(i -> str.charAt(i) != str.charAt(str.length() - 1 - i)) .findFirst().orElse(str.length()));
1赞 Alexander Ivanchenko 6/7/2022
@Holger 一个非常好的高性能解决方案。它可以通过替换来增强,因为不需要迭代超过给定字符串的中间,它已经被证明是回文 - 我们可以返回整个长度。将此解决方案纳入答案中。range(0, str.length())range(0, str.length() / 2)