Swift 允许相同字符串的快速字符串排列

Swift 允许相同字符串的快速字符串排列,swift,string,permutation,Swift,String,Permutation,我见过关于字符串排列的其他问题,但它们并没有完全涵盖我的问题 假设我有一个字符串数组:[“A”、“B”、“C”、“D”、“E”],我正在寻找一种方法来获得所有可能的组合,例如三个元素: AAA,AAB,AAC,AAD,AAE,ABA,ACA 置换的其他解决方案(例如,或)不允许重复相同的元素,并导致: ABC,ABD,ABE,BAC 我现在使用蛮力方法,迭代次数很多,但这当然非常慢(因为单个字符串的数量可能超过10个) 有没有办法解决这个问题 这就是我现在拥有的: func getVariati

我见过关于字符串排列的其他问题,但它们并没有完全涵盖我的问题

假设我有一个字符串数组:
[“A”、“B”、“C”、“D”、“E”]
,我正在寻找一种方法来获得所有可能的组合,例如三个元素:

AAA,AAB,AAC,AAD,AAE,ABA,ACA

置换的其他解决方案(例如,或)不允许重复相同的元素,并导致:

ABC,ABD,ABE,BAC

我现在使用蛮力方法,迭代次数很多,但这当然非常慢(因为单个字符串的数量可能超过10个)

有没有办法解决这个问题

这就是我现在拥有的:

func getVariations() -> [String] {
var variations = [String]()

let elements = ["A", "B", "C", "D", "E"]

for e1 in elements {
    variations.append(e1)

    for e2 in elements {
        variations.append(e1 + e2)

        for e3 in elements {
            variations.append(e1 + e2 + e3)

            for e4 in elements {
                variations.append(e1 + e2 + e3 + e4)
            }
        }
    }

    return variations
}
可以想象,当需要添加更多元素时,这会失控。

将排列视为自定义位置数字系统中的序列号 AAA、AAB、AAC、AAD、AAE、ABA、ACA等

根据您的示例,您基本上希望将唯一的单字母字符串置换为替换字符串;固定样本量(以上;3)。如果是这样的话,你可以把你的字母看作是一个自定义的数字位置数字系统中的唯一数字,具体来说你是一个基数5系统,你想计算最多3位数的所有数字。最后,如果使用的数字少于允许的数字(
String),您希望用前导的“零”(
A
)填充所有数字{ 返回计数>=宽度?自身 :字符串(重复:填充,计数:宽度-计数)+self } } 让数字=[“A”、“B”、“C”、“D”、“E”] 让base=digits.count 让宽度=3 如果让零=digits.first.map(Character.init){ //迭代并转换为数字系统。 对于0中的i..=宽度?self :字符串(重复:填充,计数:宽度-计数)+self } } 元素==字符的扩展数组{ //限制:所有元素都是唯一的(否则:`nil`return) func replacementPermute(样本大小宽度:Int)->[String]{ 保护计数==Set(self).count else{return nil} 变量置换:[字符串]=[] 如果让零=第一{ 让numPerms=((0..In,您询问如何从dfri的答案(+1)中筛选结果,以删除由不同元素顺序产生的重复项(例如,如果您得到的结果集带有“AAB”、“ABA”和“BAA”,请删除后两个)

如果您想这样做,我建议您编写一个函数,直接返回解决方案集:

扩展数组,其中元素:StringProtocol{
///返回数组元素的组合(忽略这些组合中项目的顺序)。
///
///-参数:
///-大小:要返回的组合的大小。
///-allowDuplicates:布尔值,指示数组中的项是否可以在组合中重复(例如,采样项是否返回到原始集合)。
///
///-返回:结果组合的集合。
func组合(大小:Int,allowDuplicates:Bool=false)->[字符串]{
让n=计数
如果大小>n&!allowDuplicates{return[]}
变量组合:[字符串]=[]
var指数=[0]
变量i=0
虽然是真的{
//构建索引数组(如果不完整)
而index.countn-(allowDuplicates?1:(size-index.count))
索引.附加(i)
}
}
}
因此:

将返回:

[“AA”、“AB”、“AC”、“AD”、“BB”、“BC”、“BD”、“CC”、“CD”、“DD”]

如果您不希望它允许重复:

let result=array.compositions(大小:2)
将返回:

[“AB”、“AC”、“AD”、“BC”、“BD”、“CD”]

这种方法将避免需要修改


请注意,我相信有更优雅的方法来实现上述目标,但希望这能说明基本思想。

我们不会在这里为您做功课。请用实际代码更新您的问题,并清楚地解释您在代码中遇到的问题。哇,这不是作业。我将添加我已经尝试过的当前代码。这是might帮助。我将在以后为Swift 3/4更新它。这里有更多可能的起点:。我认为如果使用幂计算,排列的数量将更容易阅读:
Int(pow(Double(base),Double(width))
。如果您只想使用整数运算,那么
repeatElement(base,count:width)。reduce(1,*)
。这肯定比我的暴力方法快。我还将尝试@Martin R建议的链接,稍后将在这里发布我的发现。@Sulthan我故意避免了
pow
和转换,但是
repeatElement
很简洁,谢谢!稍后会更新我的答案。我更喜欢这种方法h、 因为它允许我将sampleSize限制为一个值,而其他解决方案给出了所有可能的组合。换句话说,我将只得到
“AAA”,“AAA下一步是如何筛选出具有相同字母组合的结果(我不需要“AAC”和“ACA”),但如果我想不出另一个问题,我会发布另一个问题。这个答案实际上同时解决了这个问题和第二个问题(见链接)。而且它比我尝试过的任何其他解决方案都快。一个快速跟进问题:输入真的
// Helper to pad the presented numbers to a given width.
extension String {
    func leftPadded(with padding: Character, toAtLeast width: Int) -> String {
        return count >= width ? self
            : String(repeating: padding, count: width - count) + self
    }
}

let digits = ["A", "B", "C", "D", "E"]
let base = digits.count
let width = 3

if let zero = digits.first.map(Character.init) {
    // Iterate and convert to your numeral system.
    for i in 0..<((0..<width).reduce(1) { (p, _) in p * base }) {
        let nonPaddedPermutation = String(i, radix: base)
            .flatMap { Int(String($0), radix: base) }
            .map { String(digits[$0]) }
            .joined()
        print(nonPaddedPermutation.leftPadded(with: zero, toAtLeast: width))
    } /* AAA
         AAB
         ...
         EED
         EEE */
}
extension String {
    func leftPadded(with padding: Character, toAtLeast width: Int) -> String {
        return count >= width ? self
            : String(repeating: padding, count: width - count) + self
    }
}

extension Array where Element == Character {
    // Limitation: all elements are unique (otherwise: `nil` return)
    func replacementPermute(sampleSize width: Int) -> [String]? {
        guard count == Set(self).count else { return nil }

        var permutations: [String] = []
        if let zero = first {
            let numPerms = ((0..<width).reduce(1) { (p, _) in p * count })
            permutations.reserveCapacity(numPerms)
            for i in 0..<numPerms {
                let nonPaddedPermutation = String(i, radix: count)
                    .flatMap { Int(String($0), radix: count) }
                    .map { String(self[$0]) }
                    .joined()
                permutations.append(nonPaddedPermutation
                    .leftPadded(with: zero, toAtLeast: width))
            }
        }
        return permutations
    }
}

// Example usage:
if let permutations = ["A", "In another question, you ask how to filter the results from dfri's answer (+1) to prune out duplicates resulting from a different order of elements (e.g. if you got a result set with "AAB", "ABA", and "BAA", prune out the latter two).

If that's what you want to do, I'd suggest writing a function that just returned that set of solutions directly:

extension Array where Element: StringProtocol {

    /// Return combinations of the elements of the array (ignoring the order of items in those combinations).
    ///
    /// - Parameters:
    ///   - size: The size of the combinations to be returned.
    ///   - allowDuplicates: Boolean indicating whether an item in the array can be repeated in the combinations (e.g. is the sampled item returned to the original set or not).
    ///
    /// - Returns: A collection of resulting combinations.

    func combinations(size: Int, allowDuplicates: Bool = false) -> [String] {
        let n = count

        if size > n && !allowDuplicates { return [] }

        var combinations: [String] = []

        var indices = [0]

        var i = 0

        while true {
            // build out array of indexes (if not complete)

            while indices.count < size {
                i = indices.last! + (allowDuplicates ? 0 : 1)
                if i < n {
                    indices.append(i)
                }
            }

            // add combination associated with this particular array of indices

            combinations.append(indices.map { self[$0] }.joined())

            // prepare next one (incrementing the last component and/or deleting as needed

            repeat {
                if indices.count == 0 { return combinations }
                i = indices.last! + 1
                indices.removeLast()
            } while i > n - (allowDuplicates ? 1 : (size - indices.count))
            indices.append(i)
        }
    }
}
let array = ["A", "B", "C", "D"]
let result = array.combinations(size: 2, allowDuplicates: true)