【问题标题】:All possible combinations (subsets) in swiftswift中所有可能的组合(子集)
【发布时间】:2021-03-15 04:19:16
【问题描述】:

我遇到了这个问题,试图尝试使用 swift 递归地打印出字符串(字符)的给定数组的所有子集。值为 ("a1b2")。输出应该有 4 个子集。

目前卡在这里:

func overall(string: String) {
    helper(string: string, i: 0, slate: "")
}

func helper(string: String, i:  Int, slate: String) {
   
    var result = [Any]()
    let word = Array(string)
    var counter = i
    
    if string.count == counter {
        result.append(slate)
    } else {
        if word[i].isNumber {
            counter += 1
            helper(string: string, i: counter, slate: slate + String(word[i]))
        } else if word[i].isLowercase {
            counter += 1
            helper(string: string, i: counter, slate: slate + String(word[i]).uppercased())
        } else {
            counter += 1
            helper(string: string, i: counter, slate: slate + String(word[i]).lowercased())
        }
    }
    
}

overall(string: "a1b2")

我在帮助函数中创建基本案例时遇到问题。另外我不确定我是否正确使用递归。能否请您帮忙解释一下,将不胜感激。

【问题讨论】:

  • “输出应该有 8 个子集” 真的吗???我计算的远不止这些。你的 8 岁是多少?
  • 你的意思是像“a”、“a1”、“a1b”、“a1b2”、“1”、“1b”、“1b2”、“b”、“b2”、“2” ?
  • 嗯,从您在问题中所说的任何内容来看,这完全无法猜测。试着写出更清晰的问题!如果你说你想要大写/小写变体,而不是使用神秘的“子集”术语,你会花多少钱?
  • 所以问题是找到原始字符串的大小写变体。好的,现在我了解您在代码中试图实现的目标。只是和你描述的问题不符。

标签: ios swift recursive-datastructures


【解决方案1】:

我确信这在一般情况下完全没用,但只是为了好玩,这里有一个有趣的无递归解决方案,用于解决给定的特定问题,我们知道字符串正好是四个字符,我们知道uppercasedlowercased 必须应用于每个字符:

let s = "a1b2"
let arr = Array(s).map(String.init)
var result : Set<String> = []
for i in 0b0000...0b1111 {
    var tog = [Bool]()
    for sh in 0...3 { tog.append(i & 1<<sh == 0) }
    var word = ""
    for ix in 0...3 {
        let f = tog[ix] ? arr[ix].lowercased : arr[ix].uppercased
        word = word + f()
    }
    result.insert(word)
}
print(result)

【讨论】:

    【解决方案2】:

    OP 在 cmets 中澄清说,他想要原始字符串的大小写变体,而不是最初所说的“子集”

    [编辑] 我原本这里有一段关于String.count,但是,我的记忆一定是有错误的,因为Apple的文档确实指出String.count实际上是Characters的数字,这是什么无论如何,我们都希望它是。我希望我的错误没有让任何人偏离太多。

    您不需要任何计数器。您只需要第一个字符,然后递归字符串的其余部分。

    问题是,当您有一个字母作为第一个字母时,您需要在递归调用返回的所有字符串中预先添加大写和小写变体。

    基本情况在字符串的末尾,在这种情况下,您返回一个仅包含空字符串的数组。

    这是我的实现:

    func caseVariants(of s: String) -> [String]
    {
        func caseVariants(of s: Substring) -> [String]
        {
            guard let c = s.first else { return [""] } // base case
            
            let remainder = s[s.index(after: s.startIndex)...]
            let remainderVariants = caseVariants(of: remainder)
            var results: [String] = []
    
            if c.isLetter
            {
                results.append(
                    contentsOf: remainderVariants.map {
                        "\(c.uppercased())" + $0
                    }
                )
                results.append(
                    contentsOf: remainderVariants.map {
                        "\(c.lowercased())" + $0
                    }
                )
            }
            else
            {
                results.append(
                    contentsOf: remainderVariants.map { "\(c)" + $0 }
                )
            }
            
            return results
        }
        
        return caseVariants(of: s[...]).sorted()
    }
    
    
    print("Case variants:")
    for s in caseVariants(of: "a1b2") { print("\"\(s)\"") }
    

    输出是:

    Case variants:
    "A1B2"
    "A1b2"
    "a1B2"
    "a1b2"
    

    [EDIT] 在 cmets 中,OP 询问如果 .startIndex 被禁止(例如在采访中)怎么办。虽然我认为这样的限制是疯狂的,但有一个简单的解决方案,它是对我以前的代码的单行、非常合理的更改。更改此行:

            let remainder = s[s.index(after: s.startIndex)...]
    

    使用.dropFirst()

            let remainder = s.dropFirst()
    

    如果我们查看标准库中Collection协议中dropFirstimplementation

      @inlinable
      public __consuming func dropFirst(_ k: Int = 1) -> SubSequence {
        _precondition(k >= 0, "Can't drop a negative number of elements from a collection")
        let start = index(startIndex, offsetBy: k, limitedBy: endIndex) ?? endIndex
        return self[start..<endIndex]
      }
    

    我们看到dropFirst的使用将使用k的默认值1。在这种情况下,当我们已经检查到我们不在字符串的末尾时,该行

        let start = index(startIndex, offsetBy: k, limitedBy: endIndex) ?? endIndex
    

    等价于

        let start = index(after: startIndex)
    

    表示返回的子串是

        return self[index(after: startIndex)..<endIndex]
    

    这只是规范的说法:

        return self[index(after: startIndex)...]
    

    因此,使用 dropFirst() 的版本与内联完成后的原始解决方案相同。

    【讨论】:

    • 嘿Chip,这太棒了,所以应该有4个输出。因为有4种可能的结果。 a1b2, A1b2, a1B2, A1B2
    • 问题中使用术语“子集”是有问题的部分。现在我看到你在代码中试图做什么。您需要的是原始字符串的所有可能的大小写变化。
    • 你的回答太棒了。如果 swift 库函数不可用怎么办?如何解决这个问题?
    • .startIndex.endIndex 相比,他们更可能禁止.map.filter,后者在某种意义上更原始。但是,所有这些都是在StringCollection 一样符合的标准协议中定义的,因此它们都应该是公平的游戏,并且这些协议是定义String 本身的同一标准库的一部分。如果.map 被禁止,您将遍历结果并自己构建每个字符串。如果他们不允许.startIndex... 走开。你不想为这些不讲道理的人工作。
    • 更新了关于使用.dropFirst()的讨论
    猜你喜欢
    • 2014-08-01
    • 1970-01-01
    • 2012-03-27
    • 2011-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 2022-12-31
    相关资源
    最近更新 更多