给定字符串str ,任务是迭代生成给定字符串的所有不同排列。
例子:
Input: str = “bba”
Output:
abb
bab
bba
Input: str = “abc”
Output:
abc
acb
bac
bca
cab
cba
方法:长度为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。如果左==,请中断数组的最后一个索引。
- 否则增加a [left],如果a [left]
- 否则,将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
如果您希望与行业专家一起参加现场课程,请参阅《 Geeks现场课程》和《 Geeks现场课程美国》。