Swift Anagram 检查器
Posted
技术标签:
【中文标题】Swift Anagram 检查器【英文标题】:Swift Anagram checker 【发布时间】: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。比较 ***.com/questions/24102024/…。 – 无论如何,你的逻辑有缺陷:) 我使用的是 6.4。当我在两个 for...in 循环上切换到 7 beta 5 时,我得到 'String' does not have member 'Generator' 作为错误抛出。 @MartinR 【参考方案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(>) == Array(secondString.lowercaseString).sorted(>)
当前未使用的字符。那我可以用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 没有重复项以上是关于Swift Anagram 检查器的主要内容,如果未能解决你的问题,请参考以下文章