Swift中的重构解决方案

通过做HackerRank测试案例,我一直在学习编码考试,大部分时间我一直都做得很好,但是我对一些简单的案例感到困惑,当我看不到解决方案时你们都帮助我。 我正在解决这个问题:

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)  Int { return (a.reduce(0){ $0 + ($1 == word ? 1 : 0)}) } print(main()) 

在这个解决方案的20个测试用例中,我总是超时。 因此解决方案似乎解决了所有测试用例,但不在其所需的时间限制范围内。 这些都很棒,但是当你遇到这种情况时,它会非常令人沮丧。

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

任何更清洁,或更有效的解决方案将不胜感激! 谢谢!

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

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

它更简单,更容易遵循。 🙂

感谢@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()) 

非常感谢!