【问题标题】:Refactored Solution In SwiftSwift 中重构的解决方案
【发布时间】:2017-01-18 21:34:19
【问题描述】:

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

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

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

鉴于杂志上的文字和赎金信中的文字,如果他能准确地使用杂志上的整个文字复制他的赎金信,则打印 Yes;否则,打印编号。

输入格式

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

每个单词都由英文字母组成(即 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 的数组时,一半的测试用例超时。

我们将不胜感激任何更清洁或更高效的解决方案!谢谢!

【问题讨论】:

  • 你能用Foundation吗?
  • 是的。实际上,我总是使用它
  • 你会想要查看NSCountedSet。您需要做的就是为所有必需的字词(所需的消息)制作一个NSCountedSet,并为所有可用的字词(来自杂志)制作另一个NSCountedSet。然后只需检查每个唯一单词,第一组中的计数大于第二组中的计数
  • @Alexander - 好的,谢谢!我现在去看看
  • @pierce 将您的答案作为答案发布并接受它并不可耻。从表面上看,最好将您的解决方案添加到您的问题中。

标签: ios arrays swift set


【解决方案1】:

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

非常感谢!

【讨论】:

  • 看看你的a。您首先为其分配了一个新数组,但在 for 循环中,您分配了(readLine()!).components(separatedBy: " ") 的结果。第一次分配是不必要的
【解决方案2】:

我有空对您的代码进行了一些改进。我放 cmets 来解释变化:

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

移除 cmets:

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

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

【讨论】:

  • 这可以使用ransomNoteWordCounts.contains(where: { ... })进一步缩短
  • @MartinR 不错,节省了数组分配 :)
  • ...和短路。
  • @MartinR 没错,包含调用也短路了,但它并没有像这种改进的变体那样短路。我喜欢它:)
  • 这段代码将通过一些,但不是所有的测试。无论如何,NSCountedSet 必须通过终点线。感谢您发帖。
猜你喜欢
  • 2017-06-02
  • 1970-01-01
  • 1970-01-01
  • 2011-03-06
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 2017-02-19
相关资源
最近更新 更多