Swift 字谜检查器

Swift Anagram checker

我正在尝试为 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'

你应该试试

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

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

我现在将提议并讨论几个变体的 anagram 检查器的实现。为了衡量性能,我将使用不同的变体从 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 class,它允许我们将字符串表示为一组字符,每个字符都有自己的计数。如果两个字符串映射到相同的 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
}

更好但不是很好。这里,"culprits" 之一是使用原生 Swift 字符类型(来自 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)。但是,为了完整起见,让我讨论这种类型的另一种变体。

下一个替代方案利用 Swift [unichar: Int] 类型的字典来存储每个字符的重复次数,而不是 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 的普通 Objective-C 实现(对应于变体 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, Dictionary, ...) 该词只出现一次。

// 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 = [=16=] 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 秒)。

// 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
}
// 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
}

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

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
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)

    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()
    }

Swift 4.1 Function will give you 3 questions answer for Anagram :-

1. Input Strings (a,b) are Anagram ? //Bool

2. If not an Anagram then number of count require to change Characters in strings(a,b) to make them anagram ? // Int

3. If not an Anagram then list of Characters needs to be change in strings(a,b) to make them anagram ? // [Character]

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

  //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([=10=]) }
            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([=10=]) }
            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)")

Results of above exercise:-

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

Hope this 10 minutes exercise will give some support to my Swift family for solving Anagram related problems easily. :)

在Swift

中使用inout方法检查两个字符串是否是anagram
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.

我刚刚在 Swift 5.X

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

不要忘记空格

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

}