【问题标题】:Swift Anagram checkerSwift Anagram 检查器
【发布时间】:2015-11-19 12:00:03
【问题描述】:

我正在尝试为 swift 构建一个字谜检查器。这是我的代码。如果您不知道字谜检查器会检查两个字符串中是否包含相同的字符,但顺序无关紧要。

func checkForAnagram(#firstString: String, #secondString: String) -> Bool {
    var firstStringArray: [Character] = []
    var secondStringArray: [Character] = []
    /* if case matters delete the next four lines
    and make sure your variables are not constants */
    var first = firstString
    var second = secondString
    first = first.lowercaseString
    second = second.lowercaseString
    for charactersOne in first {
        firstStringArray += [charactersOne]
    }
    for charactersTwo in second {
        secondStringArray += [charactersTwo]
    }
    if firstStringArray.count != secondStringArray.count {
        return false
    } else {
        for elements in firstStringArray {
            if secondStringArray.contains(elements){
                return true
            } else {
                return false
            }
        }

}
}


var a = "Hello"
var b = "oellh"
var c = "World"
checkForAnagram(firstString: a, secondString: b)

我收到一条错误消息。

'[Character]' does not have a member 'contains'

【问题讨论】:

  • 您使用的是哪个 Xcode 版本?问题标记为 swift2,但您似乎使用的是 Xcode 6。比较 stackoverflow.com/questions/24102024/…。 – 无论如何,你的逻辑有缺陷:)
  • 我使用的是 6.4。当我在两个 for...in 循环上切换到 7 beta 5 时,我得到 'String' does not have member 'Generator' 作为错误抛出。 @MartinR

标签: ios swift swift2 anagram


【解决方案1】:

公认的答案简洁而优雅,但与其他解决方案相比效率非常低。

我现在将提出并讨论几个字谜检查器变体的实现。为了衡量性能,我将使用不同的变体从 50,000 多个单词的数组中找出给定单词的字谜。

// Variant 1: Sorting of Character
// Measured time: 30.46 s
func anagramCheck1(a: String, b: String) -> Bool {
    return a.characters.sorted() == b.characters.sorted()
}

这本质上是接受答案的解决方案,用 Swift 3 语法编写。这很慢,因为 Swift 的 String 与 NSString 不同,它是基于 Character 的,它可以正确处理 Unicode 字符。

更有效的解决方案是利用 NSCountedSet 类,它允许我们将字符串表示为一组字符,每个字符都有自己的计数。如果两个字符串映射到同一个 NSCountedSet,则它们是字谜。 注意:检查字符串长度作为先决条件使得实现总是更高效。

// Variant 2: NSCountedSet of Character
// Measured time: 4.81 s
func anagramCheck2(a: String, b: String) -> Bool {
    guard a.characters.count == b.characters.count else { return false }
    let aSet = NSCountedSet()
    let bSet = NSCountedSet()
    for c in a.characters {
        aSet.add(c)
    }
    for c in b.characters {
        bSet.add(c)
    }
    return aSet == bSet
}

更好但不是很好。在这里,“罪魁祸首”之一是使用原生 Swift Character 类型(来自 Swift 的 String)。回到旧的 Objective-C 类型(NSString 和 unichar)可以让事情变得更有效率。

// Variant 3: NSCountedSet of unichar
// Measured time: 1.31 s
func anagramCheck3(a: String, b: String) -> Bool {
    let aString = a as NSString
    let bString = b as NSString
    let length = aString.length
    guard length == bString.length else { return false }
    let aSet = NSCountedSet()
    let bSet = NSCountedSet()
    for i in 0..<length {
        aSet.add(aString.character(at: i))
        bSet.add(bString.character(at: i))
    }
    return aSet == bSet
}

使用 NSCountedSet 很好,但是在我们比较两个 NSCountedSet 对象之前,我们会完全填充它们。一个有用的替代方法是只为两个字符串中的一个完全填充 NSCountedSet,然后,当我们为另一个字符串填充 NSCountedSet 时,如果另一个字符串包含在第一个字符串的 NSCountedSet 中找不到的字符,我们会提前失败字符串。

// Variant 4: NSCountedSet of unichar and early exit
// Measured time: 1.07 s
func anagramCheck4(a: String, b: String) -> Bool {
    let aString = a as NSString
    let bString = b as NSString
    let length = aString.length
    guard length == bString.length else { return false }
    let aSet = NSCountedSet()
    let bSet = NSCountedSet()
    for i in 0..<length {
        aSet.add(aString.character(at: i))
    }
    for i in 0..<length {
        let c = bString.character(at: i)
        if bSet.count(for: c) >= aSet.count(for: c) {
            return false
        }
        bSet.add(c)
    }
    return true
}

这是我们将获得的最佳时机(使用 Swift)。不过,为了完整起见,让我再讨论一个此类变体。

下一个替代方案利用 [unichar: Int] 类型的 Swift 字典来存储每个字符的重复次数,而不是 NSCountedSet。它比前两个变体稍慢,但我们可以稍后重用它以获得更快的实现。

// Variant 5: counting repetitions with [unichar:Int]
// Measured time: 1.36
func anagramCheck5(a: String, b: String) -> Bool {
    let aString = a as NSString
    let bString = b as NSString
    let length = aString.length
    guard length == bString.length else { return false }
    var aDic = [unichar:Int]()
    var bDic = [unichar:Int]()
    for i in 0..<length {
        let c = aString.character(at: i)
        aDic[c] = (aDic[c] ?? 0) + 1
    }
    for i in 0..<length {
        let c = bString.character(at: i)
        let count = (bDic[c] ?? 0) + 1
        if count > aDic[c] ?? 0 {
            return false
        }
        bDic[c] = count
    }
    return true
}

请注意,使用 NSCountedSet 的 vanilla Objective-C 实现(对应于 Variant 3)比所有以前的版本都要快很多。

// Variant 6: Objective-C and NSCountedSet
// Measured time: 0.65 s
- (BOOL)anagramChecker:(NSString *)a with:(NSString *)b {
    if (a.length != b.length) {
        return NO;
    }
    NSCountedSet *aSet = [[NSCountedSet alloc] init];
    NSCountedSet *bSet = [[NSCountedSet alloc] init];
    for (int i = 0; i < a.length; i++) {
        [aSet addObject:@([a characterAtIndex:i])];
        [bSet addObject:@([b characterAtIndex:i])];
    }
    return [aSet isEqual:bSet];
}

我们可以改进之前尝试的另一种方法是观察,如果我们需要找到给定单词的字谜,我们不妨认为该单词是固定的,因此我们可以构建相应的结构 (NSCountedSet,字典,...)该单词仅一次。

// Finding all the anagrams of word in words
// Variant 7: counting repetitions with [unichar:Int]
// Measured time: 0.58 s
func anagrams(word: String, from words: [String]) -> [String] {
    let anagrammedWord = word as NSString
    let length = anagrammedWord.length
    var aDic = [unichar:Int]()
    for i in 0..<length {
        let c = anagrammedWord.character(at: i)
        aDic[c] = (aDic[c] ?? 0) + 1
    }
    let foundWords = words.filter {
        let string = $0 as NSString
        guard length == string.length else { return false }
        var bDic = [unichar:Int]()
        for i in 0..<length {
            let c = string.character(at: i)
            let count = (bDic[c] ?? 0) + 1
            if count > aDic[c] ?? 0 {
                return false
            }
            bDic[c] = count
        }
        return true
    }
    return foundWords
}

现在,在之前的变体中,我们使用 [unichar:Int] 字典进行计数。这证明比使用 unichar 的 NSCountedSet 稍微更有效,无论是提前退出(0.60 秒)还是不提前退出(0.87 秒)。

【讨论】:

  • 你可以把你的函数设为@inline(__always),我认为它会提高性能。无论如何,非常好的调查和很棒的答案。
  • 还将for 更改为while 也会提高性能。 for 在后台使用序列协议。使用while,我们将避免过多的函数调用。
【解决方案2】:

你应该试试

func checkForAnagram(firstString firstString: String, secondString: String) -> Bool {
    return firstString.lowercaseString.characters.sort() == secondString.lowercaseString.characters.sort()
}

【讨论】:

  • 在 Xcode 6.4 中。此代码引发错误。在 7 beta 5 版本中,这是我的代码的更紧凑版本和正确答案。谢谢你的帮助@yshilov
  • 在 6.4 中这个函数可能是return Array(firstString.lowercaseString).sorted(&gt;) == Array(secondString.lowercaseString).sorted(&gt;)
  • 当前未使用的字符。那我可以用count吗?
  • 如果两者都为空怎么办?
【解决方案3】:
// Make sure name your variables correctly so you won't confuse
// Mutate the constants parameter, lowercase to handle capital letters and the sorted them to compare both. Finally check is there are equal return true or false.

func anagram(str1: String, srt2: String)->Bool{

    let string1 = str1.lowercased().sorted()
    let string2 = srt2.lowercased().sorted()

    if string1 == string2 {
        return true
    }
    return false
}

【讨论】:

    【解决方案4】:
    // This answer also would work
    // Convert your parameters on Array, then sorted them and compare them
    func ana(str1: String, str2: String)->Bool{
    
        let a = Array(str1)
        let b = Array(str2)
    
        if a.sorted() == b.sorted() {
            return true
        }
        return false
    }
    

    【讨论】:

      【解决方案5】:
      func checkAnagrams(str1: String, str2: String) -> Bool {
              guard str1.count == str2.count else { return false }
              var dictionary = Dictionary<Character, Int>()
              for index in 0..<str1.count {
                  let value1 = str1[str1.index(str1.startIndex, offsetBy: index)]
                  let value2 = str2[str2.index(str2.startIndex, offsetBy: index)]
                  dictionary[value1] = (dictionary[value1] ?? 0) + 1
                  dictionary[value2] = (dictionary[value2] ?? 0) - 1
              }
              return !dictionary.contains(where: {(_, value) in
                  return value != 0
              })
      }
      

      时间复杂度 - O(n)

      【讨论】:

        【解决方案6】:

        不要忘记空格

             func isAnagram(_ stringOne: String, stringTwo: String) -> Bool {
                 return stringOne.lowercased().sorted().filter { $0 != " "} stringTwo.lowercased().sorted().filter { $0 != " "}
        

        }

        【讨论】:

          【解决方案7】:

          Swift 4.1 Function 将为您提供 Anagram 的 3 个问题答案:-

          1.输入字符串 (a,b) 是 Anagram 吗? //布尔

          2。如果不是字谜,那么计数需要更改字符串中的字符(a,b)以使它们成为字谜? // 整数

          3.如果不是 Anagram 那么字符列表需要在 strings(a,b) 中更改以使其成为 anagram 吗? // [字符]

          第 1 步:- 将以下函数复制并粘贴到您需要的类中:-

            //MARK:-  Anagram checker
              func anagramChecker(a:String,b:String) -> (Bool,Int,[Character]) {
                  var aCharacters = Array(a)
                  var bCharacters = Array(b)
                  var count = 0
                  var isAnagram = true
                  var replacementRequiredWords:[Character] = [Character]()
                  if aCharacters.count == bCharacters.count {
                      let listA = aCharacters.filter { !bCharacters.contains($0) }
                      for i in 0 ..< listA.count {
                          if !replacementRequiredWords.contains(listA[i]) {
                              count = count + 1
                              replacementRequiredWords.append(listA[i])
                              isAnagram = false
                          }
                      }
                      let listB = bCharacters.filter { !aCharacters.contains($0) }
                      for i in 0 ..< listB.count {
                          if !replacementRequiredWords.contains(listB[i]) {
                              count = count + 1
                              replacementRequiredWords.append(listB[i])
                               isAnagram = false
                          }
                      }
                  }else{
                      //cant be an anagram
                      count = -1
                  }
                   return (isAnagram,count,replacementRequiredWords)
              }
          

          第 2 步:- 制作两个输入字符串进行测试

          // Input Strings
          var a = "aeb"
          var b = "abs"
          

          第 3 步:- 打印结果:-

          print("isAnagram : \(isAnagram(a: a, b: b).0)")
          
          print("number of count require to change strings in anagram  : \(isAnagram(a: a, b: b).1)")//-1 will come in case of cant be a Anagram
          
          print("list of  Characters needs to be change : \(isAnagram(a: a, b: b).2)")
          

          以上练习的结果:-

          isAnagram : false
          number of count require to change strings in anagram  : 2
          list of  Characters needs to be change : ["e", "s"]
          

          希望这个 10 分钟的练习能给我的 Swift 提供一些支持 家庭轻松解决字谜相关问题。 :)

          【讨论】:

            【解决方案8】:

            我们可以使用字典来构造一个新的数据结构容器。然后按字符串的键/字符比较值。

            func anagram(str1: String, str2 : String) -> Bool {
               var dict1 = [Character: Int]()
               var dict2 = [Character:Int]()
            
               for i in str1 {
                   if let count = dict1[i] {
                       dict1[i] = count + 1
                   } else {
                       dict1[i] = 1
                   }
               }
            
               for j in str2 {
                   if let count = dict2[j] {
                      dict2[j] = count + 1
                   } else {
                      dict2[j] = 1
                   }
                }
                return dict1 == dict2 ? true : false
            }
            
            // input -> "anna", "aann"
            // The count will look like: 
            // ["a": 2, "n": 2] & ["a": 2, "n": 2] 
            // then return true
            

            【讨论】:

              【解决方案9】:

              在 Swift 中使用 inout 方法检查两个字符串是否为字谜

              func checkAnagramString(str1: inout String, str2: inout String)-> Bool{
              
                var result:Bool = false
                str1 = str1.lowercased().trimmingCharacters(in: .whitespace)
                str2 = str2.lowercased().trimmingCharacters(in: .whitespaces)
              
                if (str1.count != str2.count) {
                   return result
                }
              
                for c in str1 {
                   if str2.contains(c){
                       result = true
                  }
                  else{
                      result = false
                      return result
                  }
                }
                 return result
              }
              

              调用函数检查字符串是否为字谜

               var str1 = "tommarvoloriddle"
               var str2 = "iamlordvoldemort"
              
               print(checkAnagramString(str1: &str1, str2: &str2)) //Output = true.
              

              【讨论】:

                【解决方案10】:

                我刚刚意识到在 Swift 5.X

                中执行 Anagram 函数的另一个简单方法
                func checkForAnagram(firstString firstString: String, secondString: String) -> Bool {
                    return !firstString.isEmpty && firstString.sorted() == secondString.sorted()
                }
                

                【讨论】:

                  【解决方案11】:
                      func isAnagram(word1: String, word2: String) -> Bool {
                          let set1 = Set(word1)
                          let set2 = Set(word2)
                          return set1 == set2
                      }
                  
                  or
                  
                      func isAnagram(word1: String,word2: String) -> Bool {
                          return word1.lowercased().sorted() == word2.lowercased().sorted()
                      }
                  

                  【讨论】:

                  • 第一个不行,用 "settt" : "tes" 试试,它会返回 true。并且不是 Anagram,因为 Set 没有重复项
                  猜你喜欢
                  • 2020-05-21
                  • 1970-01-01
                  • 2015-02-08
                  • 2016-03-14
                  • 2011-02-11
                  • 2020-06-25
                  • 2018-08-25
                  • 1970-01-01
                  • 2018-12-23
                  相关资源
                  最近更新 更多