📜  生成字符串的不同置换的迭代程序

📅  最后修改于: 2021-05-06 09:39:43             🧑  作者: Mango

给定字符串str ,任务是迭代生成给定字符串的所有不同排列。

例子:

方法:长度为n的字符串的排列数目为n!。以下算法也可以扩展到任意对象的数组。这里仅涉及生成字符串置换的情况。

该算法以迭代方式生成它们,如下所示:
我们将使用示例输入字符串“ abca”完成所有步骤。

该算法的第一步将每个字母映射到一个数字,并存储每个字母的出现次数。为此,首先对字符串进行排序。

修改后的输入字符串:“ aabc”

现在,字母将被映射如下:

(first: mapped Number, second: Count)
a : 0, 2
b : 1, 1
c : 2, 0

我们使用的数字系统的底数将为3(等于输入字符串中不同字符的数量)
现在,一个数组将保存每个字母的映射值。基本思想是在数字系统中将数组作为组合数字递增,其基数等于不同字符的数量。

因此,最初的数组为[0,0,1,2]
我们向其添加1,新数组为[0,0,2,0](数字系统为3:我们在基数为3的系统中简单地添加了0012和1)

我们检查数组中每个数字的出现次数,并与原始引用数组中每个字母的出现次数进行检查。在这种情况下,[0,0,2,0]是无效序列,因为原始字符串的count(0)>零个数(或“ a”,映射到0的字符)

我们重复此过程,直到我们的数组等于[2,1,0,0],即可以生成的最大有效数字,并因此耗尽所有排列。
这种方法的复杂度为O(n (n + 1) )

优化算法

基本前提仍然保持不变。然而,现在,该算法试图避免许多不相关且无用的状态或置换。

函数generatePermutation(字符串)准备一个参考表(代码中的ref向量),该表存储每个索引及其出现位置的映射字符。它还准备映射整数的字。 (要置换的整数数组)。

该函数调用函数getNext(args…),该函数将数组更改为下一个排列。当发生溢出(生成所有排列)时,该函数将返回END。否则,它返回VALID。

因此,要获得下一个状态,我们首先要像前面的方法那样将数组增加1。我们跟踪由于添加而更改的索引范围。

例子:
递增[0、2、2、2]会得到[1、0、0、0],其索引范围为更改的{1、2、3}或[1、3]。之后,我们从最左边的索引开始,检查它是否是有效数字。
如果在左之前的子字符串(即[0,left))的实例a [left]的值严格小于n,则该数字有效,其中n是原始数组中a [left]的出现次数。

  1. 检查左侧是否有效。
  2. 如果没有冲突,则向左递增,请转到步骤1。如果左==,请中断数组的最后一个索引。
  3. 否则增加a [left],如果a [left]
  4. 否则,将1添加到子数组[0,left]并获得新的left索引,该索引开始更改零件的范围。转到步骤1。

如果生成的排列有效,则该函数返回VALID;如果发生溢出,则返回END。

有效性检查是通过辅助函数hasConflict(args…)完成的,该函数仅检查子数组[0,left)是否出现a [left]。
如果出现次数严格小于原始数组中a [left]的出现次数,则它返回true,否则返回false。

通过首先检查并添加到左侧的索引,我们跳过了许多本来会在算法中爬升的状态。
该算法为每个状态最多经历O(n)个状态。
因此,时间复杂度: O(n * n!)
空间复杂度: O(n)

下面是上述方法的实现:

// C++ implementation of the approach
#include 
using namespace std;
  
// No more permutations can be generated
#define END -1
  
// Permutation is valid
#define VALID 1
  
// Utility function to print the
// generated permutation
void printString(vector& word,
                 vector >& ref)
{
  
    for (int i = 0; i < word.size(); i++) {
        cout << ref[word[i]].first;
    }
    cout << "\n";
}
  
// Function that checks for any conflict present
// in the word/string for the index
bool hasConflict(vector& word,
                 vector >& ref, int index)
{
  
    // Check number of instances where value of
    // the character is equal to the
    // value at checking index
    int conflictCount = 0;
    for (int i = 0; i < index; i++) {
        if (word[i] == word[index])
            conflictCount++;
    }
  
    // If the number of instances are greater
    // than the number of occurrences of the
    // character then conflict is present
    if (conflictCount < ref[word[index]].second) {
        return false;
    }
  
    return true;
}
  
// Function that returns the validity of the
// next permutation generated and makes
// changes to the word array
int getNext(vector& word,
            vector >& ref, int mod)
{
  
    int left, right, carry;
  
    right = word.size() - 1;
  
    carry = 1;
  
    // Simply add 1 to the array word following
    // the number system with base mod
    // generates a new permutation
    for (left = right; left >= 0; left--) {
        if (left == 0 && word[left] + carry >= mod) {
            return END; // overflown
        }
  
        if (word[left] + carry >= mod) {
            word[left] = (word[left] + carry) % mod;
        }
        else {
            word[left] = word[left] + carry;
            break;
        }
    }
  
    // All the values between left and right (inclusive)
    // were changed and therefore need to be
    // adjusted to get a valid permutation
    while (left <= right) {
        while (hasConflict(word, ref, left) && word[left] < mod) {
  
            // Increment till conflict between substring [0, i)
            // is resolved or word[left] goes above mod
            word[left]++;
        }
        if (word[left] >= mod) {
  
            // The value for left has crossed mod therefore
            // all the values for word with [0, left)
            // constant have been convered therefore add 1
            // to the substring [0, left) to get a new value
            // of left which represents all affected parts.
            // Repeat the process of conflict resolvation
            // and validity checking from this new left
            word[left] %= mod;
            int carry = 1;
            left--;
            while (left >= 0) {
                if (left == 0 && word[left] + carry >= mod) {
  
                    // Overflow
                    return END;
                }
  
                if (word[left] + carry >= mod) {
                    word[left] = (word[left] + carry) % mod;
                    left--;
                }
                else {
                    word[left] = (word[left] + carry);
                    break;
                }
            }
        }
        else {
  
            // Increment left if conflict is resolved
            // for current index and do conflict
            // resolution for the next index
            left++;
        }
    }
  
    return VALID;
}
  
// Iterative function to generate all the
// distinct permutations of str
void generatePermutations(string str)
{
    if (str.size() == 0)
        return;
  
    // First sort the string to assign mapped values
    // and occurrences to each letter
    // Sorting needs to handle letters
    // with multiple occurrences
    sort(str.begin(), str.end());
  
    // Reference vector to store the mapping of
    // its index to its corresponding char
    // and the number of occurrences of the character
    // as the second element of the pair
    vector > ref(str.size());
  
    // Assign each character its index and the
    // number of occurrences
    int count = 0;
    ref[count] = make_pair(str[0], 1);
  
    for (int i = 1; i < str.size(); i++) {
  
        // Increment occurrences if character is repeated
        // Else create new mapping for the next character
        if (str[i] == str[i - 1]) {
            ref[count].second++;
        }
        else {
            count++;
            ref[count] = make_pair(str[i], 1);
        }
    }
  
    // Size may differ in case of multiple
    // occurrences of characters
    ref.resize(count + 1);
  
    // Construct the word
    // Word stores the mapped values for every letter
    // in a permuted sequence i.e. say for "abc"
    // word would be initially [0, 1, 2] or "aba", [0, 1, 0]
    vector word;
    for (int i = 0; i < ref.size(); i++) {
        for (int j = 0; j < ref[i].second; j++)
            word.push_back(i);
    }
  
    // mod is the number of distinct letters in string
    int mod = ref.size();
    int flag = VALID;
  
    while (flag != END) {
  
        // If the permutation sent by getNext
        // is valid then print it
        printString(word, ref);
  
        // Get the next permutation, validity
        // stored in flag
        flag = getNext(word, ref, mod);
    }
}
  
// Driver code
int main()
{
    string str = "abc";
  
    generatePermutations(str);
  
    return 0;
}
输出:
abc
acb
bac
bca
cab
cba