【问题标题】:How to find the longest common prefix of two strings in Scala?如何在Scala中找到两个字符串的最长公共前缀?
【发布时间】:2011-11-12 12:37:13
【问题描述】:

如何在Scala中找到两个字符串的最长公共前缀?

我可能可以编写一个“命令式”解决方案(索引i 在字符串上运行,而s(i) == t(i))但我正在寻找一个“功能式”解决方案(例如,无需显式更新索引变量)。

【问题讨论】:

    标签: string scala list-comprehension


    【解决方案1】:
    scala> "helloworld".zip("hellohell").takeWhile(Function.tupled(_ == _)).map(_._1).mkString
    res130: String = hello
    

    【讨论】:

    • 压缩整个字符串有点浪费,因为公共前缀可能只有几个字符
    • 如果您将"helloworld".zip("hellohell") 更改为("helloworld", "hellohell").zipped,我认为可以解决低效率问题。
    • @DavidY.Ross:+1。或"helloworld".view zip "hellohell".
    • 我建议使用takeWhile { case (a, b) => a == b } 而不是takeWhile(Function.tupled(_ == _))
    • @XavierGuihot,当然。我认为这是一种风格偏好。
    【解决方案2】:

    另一个递归版本。

    def pref(s: String, t: String, out: String = ""): String = {
      if (s == "" || t == "" || s(0) != t(0)) out
      else pref(s.substring(1), t.substring(1), out + s(0))
    }
    

    它比 sjj 快 10 倍以上,比 missingfaktor 快两倍以上。 Java 的 substring 很快,因为 String 是不可变的。

    【讨论】:

      【解决方案3】:

      递归版本:

      def findCommonPrefix(s1 : String, s2 : String) : String = {
          def findCommonPrefixR(l1: List[Char], l2 : List[Char]) : List[Char] = {
              l1 match {
              case Nil => Nil
              case x::xs => if (l2 != Nil && l2.head == x) x :: findCommonPrefixR(xs, l2.tail) else Nil
              }
          }
          findCommonPrefixR(s1.toList, s2.toList).mkString
      }
      

      【讨论】:

      • Luigi 提到这一点,他的速度比我的快 10 倍以上。这实际上取决于。对于没有很多匹配字符的字符串,他的速度更快。但是,对于可能有更多匹配字符的字符串,我的速度实际上是他的 2 倍以上。
      • Luigi,尝试计时:pref("这是一个长度有点、适度、有点长的字符串"、"这是一个长度有点、适度、有点长的字符串")
      • 我承认我只用一个测试字符串做了一个非常快速和不彻底的基准测试。运行 64 位 JVM 时,您的性能会提高很多。正如missingfaktor 的命令式版本所示,两者在机器操作方面仍然非常低效。
      【解决方案4】:

      命令式版本可以简化为

      def longestCommonPrefix(s1:String, s2:String):String = {
        val maxSize = scala.math.min(s1.length, s2.length)
        var i:Int = 0;
        while ( i < maxSize && s1(i)== s2(i)) i += 1;
        s1.take(i);
      }
      

      【讨论】:

        【解决方案5】:

        如果速度很重要,那就势在必行。

        scala> def longestCommonPrefix(a: String, b: String): String = {
             |   var same = true
             |   val sb = new StringBuilder
             |   var i = 0
             |   while(same && i < math.min(a.length, b.length)) {
             |     if(a.charAt(i) != b.charAt(i)) {
             |       same = false
             |     } else {
             |       sb += a.charAt(i)
             |       i += 1
             |     }
             |   }
             |   sb.result
             | }
        longestCommonPrefix: (a: String, b: String)String
        
        scala> longestCommonPrefix("", "")
        res50: String = ""
        
        scala> longestCommonPrefix("helloworld", "hellohell")
        res51: String = hello
        

        编辑:

        按照路易吉的建议:

        scala> def longestCommonPrefix(a: String, b: String): String = {
             |   var same = true
             |   var i = 0
             |   while(same && i < math.min(a.length, b.length)) {
             |     if(a.charAt(i) != b.charAt(i)) {
             |       same = false
             |     } else {
             |       i += 1
             |     }
             |   }
             |   a.substring(0, i)
             | }
        longestCommonPrefix: (a: String, b: String)String
        
        scala> longestCommonPrefix("helloworld", "hellohell")
        res68: String = hello
        

        【讨论】:

        • 不使用 StringBuilder 只计算索引,然后返回 a.substring(0, i)。这要快得多,因为您根本不必构建新字符串。
        • 很好的建议。谢谢!
        【解决方案6】:

        获取任意数量字符串的公共前缀:

        def commonPrefix (strs: Seq[String]): String = {
          var i = 0;
          strs(0).takeWhile { ch => strs.forall(_(i) == ch) && {i += 1; true}} mkString
        } 
        

        【讨论】:

        • 不完全...它会因溢出而失败。您需要检查最短字符串的长度。
        【解决方案7】:
        def commonPrefix(strings: String*): String = {
          val refString = strings.map(s => (s, s.length())).minBy { case (string, length) => length }._1
          var i = 0
          while (i < refString.length) {
            if (!strings.forall(_(i) == refString(i))) return refString.substring(0, i)
            i += 1
          }
          return refString
        }
        

        【讨论】:

          【解决方案8】:

          根据 OP 请求,下面是一个简单的最佳快速函数式实现。

          这种实现与任何手写的可变命令式风格一样快(因为它是尾递归的)。

          def takeCommonPrefix(string1: String, string2: String): String = {
            if (string1.nonEmpty && string2.nonEmpty) {
              val (shorter, longer) =
                if (string1.length < string2.length) (string1, string2)
                else (string2, string1)
          
              @scala.annotation.tailrec
              def recursive(shorterIndex: Int = 0): String =
                if (shorterIndex == shorter.length)
                  shorter
                else
                  if (shorter(shorterIndex) != longer(shorterIndex))
                    shorter.take(shorterIndex)
                  else
                    recursive(shorterIndex + 1)
          
              recursive()
            }
            else
              ""
          }
          

          作为奖励,如果您想要通用前缀,那么很有可能需要通用后缀。

            def takeCommonSuffix(string1: String, string2: String): String = {
              if (string1.nonEmpty && string2.nonEmpty) {
                val (shorter, longer) =
                  if (string1.length < string2.length) (string1, string2)
                  else (string2, string1)
                val longerOffset =
                  longer.length - shorter.length
          
                @scala.annotation.tailrec
                def recursive(shorterIndex: Int = shorter.length - 1): String =
                  if (shorterIndex == -1)
                    shorter
                  else
                    if (shorter(shorterIndex) != longer(shorterIndex + longerOffset))
                      shorter.takeRight(shorter.length - shorterIndex - 1)
                    else
                      recursive(shorterIndex - 1)
          
                recursive()
              }
              else
                ""
            }
          

          【讨论】:

            猜你喜欢
            • 2011-10-21
            • 2017-08-03
            • 2012-01-24
            • 2020-11-13
            • 2021-02-14
            • 2012-02-25
            • 2011-12-23
            • 1970-01-01
            • 2018-05-07
            相关资源
            最近更新 更多