【问题标题】:Anagram Recursion Scala字谜递归Scala
【发布时间】:2020-05-05 21:11:09
【问题描述】:

这是我试图为其编写代码的问题。


考虑一个递归算法,它将两个字符串 s1s2 作为输入,并检查这些字符串是否是彼此的字谜,因此如果所有字母前者包含在后者中出现的次数相同,反之亦然(即s2s1的排列)。

例子:

如果 s1 = ”elevenplustwo” 和 s2 = “twelveplusone” 输出为真

如果 s1 = ”amina” 和 s2 = “minia” 输出为假

提示: 考虑 s1 的第一个字符 c = s1(0) 和 s1 的其余部分 r =s1.substring(1, s1.size)。对于 c 和 r,s2 必须(递归地)满足什么条件?


这是我为解决这个问题而编写的一段代码。问题是当字符串中没有重复字符时,代码可以完美运行。例如,它适用于 aminmina。但是,当有重复时,例如 aminamaina,则无法正常工作。

我该如何解决这个问题?

import scala.collection.mutable.ArrayBuffer

object Q32019 extends App {
def anagram(s1:String, s2:String, indexAr:ArrayBuffer[Int]):ArrayBuffer[Int]={

  if(s1==""){
    return indexAr
  }
  else {
  val c=s1(0)
  val s=s1.substring(1,s1.length)
    val ss=s2
    var count=0
   for (i<-0 to s2.length-1) {
    if(s2(i)==c && !indexAr.contains(s2.indexOf(c))) {
      indexAr+=i
      }
    }

    anagram(s,s2,indexAr)
  }
indexAr
}
  var a="amin"
  var b="mina"
  var c=ArrayBuffer[Int]()
  var d=anagram(a,b,c)
  println(d)
  var check=true
  var i=0
  while (i<a.length && check){
    if (d.contains(i) && a.length==b.length) check=true
    else check=false
    i+=1
  }
  if (check) println("yes they are anagram")
  else println("no, they are not anagram")
}

【问题讨论】:

    标签: scala recursion anagram


    【解决方案1】:

    最简单的方法可能是对两个字符串进行排序并进行比较:

    def areAnagram(str1: String, str2: String): Boolean =
      str1.sorted == str2.sorted
    
    println(areAnagram("amina", "anima")) // true
    println(areAnagram("abc", "bcc"))     // false
    

    另一种更“自然”。如果两个字符串每个字符的计数相同,则它们是字谜。
    所以你制作两个Map[Char, Int] 并比较它们:

    import scala.collection.mutable
    
    def areAnagram(str1: String, str2: String): Boolean = {
      val map1 = mutable.Map.empty[Char, Int].withDefaultValue(0)
      val map2 = mutable.Map.empty[Char, Int].withDefaultValue(0)
      for (c <- str1) map1(c) += 1
      for (c <- str2) map2(c) += 1
      map1 == map2
    }
    

    如果您知道字符只是 ASCII 字符,则可能还有另一个版本的第二个解决方案 Arrays。
    或者其他一些聪明的算法,IDK。


    EDIT:一种递归解决方案是从 str2 中删除 str1 的第一个字符。两个字符串的其余部分也必须是字谜。
    例如。对于("amina", "niama"),首先你从两者中抛出一个a,然后你得到("mina", "nima")。根据定义,这两个字符串也必须是字谜

    def areAnagram(str1: String, str2: String): Boolean = {
    
      if (str1.length != str2.length) false
      else if (str1.isEmpty) true // end recursion
      else {
        val (c, r1) = str1.splitAt(1)
        val r2 = str2.replaceFirst(c, "") // remove c
        areAnagram(r1, r2)
      }
    }
    

    【讨论】:

    • 谢谢。当然排序和比较是最好的解决方案,但问题是我需要使用问题提供的提示,并且使用递归很重要。
    • @user12743563 我还添加了一个递归解决方案。 :D
    【解决方案2】:

    当你计算字谜时,你可以利用 XOR 运算的特性,即如果你对两个相同的数字进行异或运算,你会得到 0。

    由于字符串中的字符本质上只是数字,因此您可以对两个字符串的所有字符进行异或运算,如果结果为 0,则这些字符串是字谜。

    您可以使用循环遍历两个字符串,但如果您想使用递归,我建议您将字符串转换为字符列表。

    列表允许在第一个元素(列表的头部)和其余元素(列表的尾部)之间进行有效拆分。所以解决方案是这样的:

    1. 将两个字符列表拆分为头部和尾部。
    2. 对从列表头部和先前结果中提取的字符进行异或运算。
    3. 将列表尾部和异或结果传递给下一个递归调用。

    当我们到达列表的末尾时,我们只返回 true 是 xoring 的 case 结果为 0

    我们可以做的最后一个优化是在传递不同长度的字符串时使用 false 进行短循环(因为它们永远不可能是字谜)。

    最终解决方案:

    def anagram(a: String, b: String): Boolean = {
    
      //inner function doing recursion, annotation @tailrec makes sure function is tail-recursive
      @tailrec
      def go(a: List[Char], b: List[Char], acc: Int): Boolean = { //using additional parameter acc, allows us to use tail-recursion, which is safe for stack
        (a, b) match {
          case (x :: xs, y :: ys) => //operator :: splits list to head and tail
            go(xs, ys, acc ^ x ^ y) //because we changed string to lists of chars, we can now efficiently access heads (first elements) of lists
          //we get first characters of both lists, then call recursively go passing tails of lists and result of xoring accumulator with both characters
          case _ => acc == 0 //if result of xoring whole strings is 0, then both strings are anagrams
        }
      }
    
      if (a.length != b.length) { //we already know strings can't be anagrams, because they've got different size
        false
      } else {
        go(a.toList, b.toList, 0)
      }
    }
    
    anagram("twelveplusone", "elevenplustwo") //true
    anagram("amina", "minia") //false
    

    【讨论】:

      【解决方案3】:

      我的建议:不要想太多。

      def anagram(a: String, b: String): Boolean =
        if (a.isEmpty) b.isEmpty
        else b.contains(a(0)) && anagram(a.tail, b diff a(0).toString)
      

      【讨论】:

        猜你喜欢
        • 2018-05-18
        • 2015-12-28
        • 2019-02-06
        • 2015-07-03
        • 2019-01-15
        • 2018-03-08
        • 2018-03-17
        • 1970-01-01
        • 2014-04-03
        相关资源
        最近更新 更多