Swift中的重构解决方案



我一直在通过HackerBank测试用例学习编码考试,在大多数情况下我都做得很好,但我被一些简单的用例所困扰,当我看不到解决方案时,你们都会帮助我。我正在处理这个问题:

https://www.hackerrank.com/challenges/ctci-ransom-note

一名绑匪写了一张赎金单,但担心会被追查到他。他找到了一本杂志,想知道是否可以从中剪下完整的单词,并用它们制作一个无法追踪的赎金单复制品。他的笔记中的单词区分大小写,他必须使用杂志上的完整单词,这意味着他不能使用子串或串联来创建他需要的单词。

考虑到杂志上的文字和赎金单上的文字,如果他能准确地使用杂志上的全部文字复制他的赎金单,请打印"是";否则,打印编号

输入格式

第一行包含两个空格分隔的整数,分别描述(杂志中的字数)和(赎金单中的单词数)的值。第二行包含空格分隔的字符串,表示杂志中出现的单词。第三行包含空格分隔的字符串,表示赎金单中的单词。

每个单词都由英文字母组成(即to和to)。笔记和杂志中的文字区分大小写。输出格式

如果他可以使用杂志制作一个无法追踪的赎金单复制品,请打印"是";否则,打印编号

样本输入

6 4
give me one grand today night
give one grand today

样本输出

Yes
Explanation

写一张无法追踪的赎金单副本所需的四个字都在杂志上,所以我们打印"是"作为答案。

这是我的解决方案:

import Foundation
func main() -> String {
let v = readLine()!.components(separatedBy: " ").map{Int($0)!}
var a = [String](); var b = [String]()
if v[0] < v[1] { return "No"}
for i in 0 ..< 2 {
if i == 0 {
a = (readLine()!).components(separatedBy: " ")
} else { b = (readLine()!).components(separatedBy: " ") }
}
// Get list of elements that intersect in each array
let filtered = Set(a).intersection(Set(b))
// Map set to set of Boolean where true means set a has enough words to satisfy set b's needs
let checkB = filtered.map{ word in reduceSet(b, word: word) <= reduceSet(a, word: word) }
// If mapped set does not contain false, answer is Yes, else No
return !checkB.contains(false) ? "Yes" : "No"
}
func reduceSet(_ a: [String], word: String) -> Int {
return (a.reduce(0){ $0 + ($1 == word ? 1 : 0)})
}
print(main())

使用这个解决方案,我总是在20个测试用例中的三个测试用例上超时。因此,该解决方案似乎解决了所有的测试用例,但不在它们所需的时间限制内。这些都是很好的练习,但当你陷入这种困境时,会非常沮丧。

我应该注意,我使用SetsSet(a).intersection(Set(b)),因为当我尝试映射Strings的数组时,一半的测试用例超时。

任何更清洁或更高效的解决方案都将不胜感激!非常感谢。

感谢@Alexander-我能够使用NSCountedSet而不是我的自定义reduce方法来解决这个问题。它更干净、更高效。以下是解决方案:

import Foundation
func main() -> String {
let v = readLine()!.components(separatedBy: " ").map{Int($0)!}
var a = [String](); var b = [String]()
if v[0] < v[1] { return "No"}
for i in 0 ..< 2 {
if i == 0 {
a = (readLine()!).components(separatedBy: " ")
} else { b = (readLine()!).components(separatedBy: " ") }
}
let countA = NSCountedSet(array: a)
let countB = NSCountedSet(array: b)
let intersect = Set(a).intersection(Set(b))
let check = intersect.map{ countB.count(for: $0) <= countA.count(for: $0) }
return !check.contains(false) ? "Yes" : "No"
}
print(main())

非常感谢!

我花了一些时间对您的代码进行了改进。我发表评论来解释这些变化:

import Foundation
func main() -> String {
// Give more meaningful variable names
let firstLine = readLine()!.components(separatedBy: " ").map{Int($0)!}
let (magazineWordCount, ransomNoteWordCount) = (firstLine[0], firstLine[1])
// a guard reads more like an assertion, stating the affirmative, as opposed to denying the negation.
// it also 
guard magazineWordCount > ransomNoteWordCount else { return "No" }
// Don't use a for loop if it only does 2 iterations, which are themselves hardcoded in.
// Just write the statements in order.
let magazineWords = readLine()!.components(separatedBy: " ")
let ransomNoteWords = readLine()!.components(separatedBy: " ") //You don't need ( ) around readLine()!
let magazineWordCounts = NSCountedSet(array: magazineWords)
let ransomNoteWordCounts = NSCountedSet(array: ransomNoteWords)
// intersect is a verb. you're looking for the noun, "intersection"
// let intersection = Set(a).intersection(Set(b))
// let check = intersect.map{ countB.count(for: $0) <= countA.count(for: $0) }
// You don't actually care for the intersection of the two sets.
// You only need to worry about exactly the set of words that
// exists in the ransom note. Just check them directly.
let hasWordWithShortage = ransomNoteWordCounts.contains(where: { word in
magazineWordCounts.count(for: word) < ransomNoteWordCounts.count(for: word)
})
// Don't negate the condition of a conditional expression. Just flip the order of the last 2 operands.
return hasWordWithShortage ? "No" : "Yes"
}
print(main())

删除评论:

import Foundation
func main() -> String {
let firstLine = readLine()!.components(separatedBy: " ").map{Int($0)!}
let (magazineWordCount, ransomNoteWordCount) = (firstLine[0], firstLine[1])
guard magazineWordCount > ransomNoteWordCount else { return "No" }
let magazineWords = readLine()!.components(separatedBy: " ")
let ransomNoteWords = readLine()!.components(separatedBy: " ")
let magazineWordCounts = NSCountedSet(array: magazineWords)
let ransomNoteWordCounts = NSCountedSet(array: ransomNoteWords)
let hasWordWithShortage = ransomNoteWordCounts.contains{ word in
magazineWordCounts.count(for: word) < ransomNoteWordCounts.count(for: word)
}
return hasWordWithShortage ? "No" : "Yes"
}
print(main())

它更简单,也更容易理解。:)

最新更新