正如Martin R所建议的,您可以使用递归
这是功能
func visit(alphabet:[Character], combination:[Character], inout combinations:[String], length: Int) {
guard length > 0 else {
combinations.append(String(combination))
return
}
alphabet.forEach {
visit(alphabet, combination: combination + [$0], combinations: &combinations, length: length - 1)
}
}
辅助函数
func combinations(alphabet: String, length: Int) -> [String] {
var combinations = [String]()
visit([Character](alphabet.characters), combination: [Character](), combinations: &combinations, length: length)
return combinations
}
测试
现在,如果您想要 3 个字符的每个组合,并且您想要"ab"
作为字母表,那么
combinations("ab", length: 3) // ["aaa", "aab", "aba", "abb", "baa", "bab", "bba", "bbb"]
重复
请注意,如果您在字母表中插入重复项,您将在结果中获得重复的元素。
时间复杂度
该visit
函数被调用的次数与节点进入k-ary
具有高度的完美树一样多,h
其中:
k
alphabet
:参数中的元素数量
h
:length
参数
这样的树有

节点。这是函数将被调用的确切次数。
空间复杂度
理论上为执行访问同时分配的最大栈帧数为length
。
然而,由于 Swift 编译器确实实现了尾调用优化,因此分配的堆栈帧数仅为 1。
最后,我们必须考虑combinations
将与结果的数量一样大:alphabet^length
所以时间复杂度是 和 的length
最大值elements into the result
。
它是O(length + alphabet^length)
更新
事实证明,您想要一个蛮力密码破解器。
func find(alphabet:[Character], combination:[Character] = [Character](), length: Int, check: (keyword:String) -> Bool) -> String? {
guard length > 0 else {
let keyword = String(combination)
return check(keyword: keyword) ? keyword : nil
}
for char in alphabet {
if let keyword = find(alphabet, combination: combination + [char], length: length - 1, check: check) {
return keyword
}
}
return nil
}
最后一个参数check
是一个闭包,用于验证当前单词是否是正确的密码。你将把你的逻辑放在这里,find
一旦找到密码就会停止。
例子
find([Character]("tabcdefghil".characters), length: 3) { (keyword) -> Bool in
return keyword == "cat" // write your code to verify the password here
}