【问题标题】:Scala/functional/without libs - check if string permutation of otherScala/functional/without libs - 检查其他字符串是否排列
【发布时间】:2017-04-20 02:54:14
【问题描述】:

如果没有复杂的预构建函数(如 sorted()),如何使用 scala/函数式编程检查一个字符串是否是另一个字符串的排列?

我是一名 Python 开发人员,我认为最让我感到困惑的是,与另一个字符计数字典相比,您不能只遍历字符计数字典,然后 在没有字符计数时退出'一场比赛,你不能只叫break

【问题讨论】:

  • 制作一个函数并从函数中返回;这应该实现与“break”相同的目标。

标签: algorithm scala functional-programming


【解决方案1】:

根据您的描述,假设这是起点:

val a = "aaacddba"
val b = "aabaacdd"
def counts(s: String) = s.groupBy(identity).mapValues(_.size)
val aCounts = counts(a)
val bCounts = counts(b)

这是最简单的方法:

aCounts == bCounts  // true

这正是你所描述的:

def isPerm(aCounts: Map[Char,Int], bCounts: Map[Char,Int]): Boolean = {
  if (aCounts.size != bCounts.size)
    return false
  for ((k,v) <- aCounts) {
    if (bCounts.getOrElse(k, 0) != v)
      return false
  }
  return true
}

这是你的方法,但更 scala-ish。 (一旦发现不匹配,它也会中断,因为foreach 是如何实现的):

(aCounts.size == bCounts.size) && 
    aCounts.forall { case (k,v) => bCounts.getOrElse(k, 0) == v }

(另外,Scala does have break。)

另外,还有:您应该阅读this question 的答案。

【讨论】:

    【解决方案2】:

    另一个使用递归函数的选项,一旦检测到不匹配,它也会立即“中断”:

    import scala.annotation.tailrec
    
    @tailrec
    def isPerm1(a: String, b: String): Boolean = {
      if (a.length == b.length) {
        a.headOption match {
          case Some(c) =>
            val i = b.indexOf(c)
            if (i >= 0) {
              isPerm1(a.tail, b.substring(0, i) + b.substring(i + 1))
            } else {
              false
            }
          case None => true
        }
      } else {
        false
      }
    }
    

    出于我自己的好奇心,我还创建了另外两个使用字符计数映射进行匹配的版本:

    def isPerm2(a: String, b: String): Boolean = {
      val cntsA = a.groupBy(identity).mapValues(_.size)
      val cntsB = b.groupBy(identity).mapValues(_.size)
      cntsA == cntsB
    }
    

    def isPerm3(a: String, b: String): Boolean = {
      val cntsA = a.groupBy(identity).mapValues(_.size)
      val cntsB = b.groupBy(identity).mapValues(_.size)
      (cntsA == cntsB) && cntsA.forall { case (k, v) => cntsB.getOrElse(k, 0) == v }
    }
    

    并通过以下方式大致比较它们的性能:

    def time[R](block: => R): R = {
      val t0 = System.nanoTime()
      val result = block    // call-by-name
      val t1 = System.nanoTime()
      println("Elapsed time: " + (t1 - t0) + "ns")
      result
    }
    
    // Match
    time((1 to 10000).foreach(_ => isPerm1("apple"*100,"elppa"*100)))
    time((1 to 10000).foreach(_ => isPerm2("apple"*100,"elppa"*100)))
    time((1 to 10000).foreach(_ => isPerm3("apple"*100,"elppa"*100)))
    
    // Mismatch
    time((1 to 10000).foreach(_ => isPerm1("xpple"*100,"elppa"*100)))
    time((1 to 10000).foreach(_ => isPerm2("xpple"*100,"elppa"*100)))
    time((1 to 10000).foreach(_ => isPerm3("xpple"*100,"elppa"*100)))
    

    结果是:

    匹配案例

    • isPerm1 = 2337999406ns
    • isPerm2 = 383375133ns
    • isPerm3 = 382514833ns

    不匹配案例

    • isPerm1 = 29573489ns
    • isPerm2 = 381622225ns
    • isPerm3 = 417863227ns

    正如预期的那样,字符计数映射加快了正例,但可以减慢负例(构建字符计数映射的开销)。

    【讨论】:

      猜你喜欢
      • 2018-08-27
      • 1970-01-01
      • 1970-01-01
      • 2019-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-06
      • 2017-01-01
      相关资源
      最近更新 更多