字符串字母的排列:如何去除重复排列?
Posted
技术标签:
【中文标题】字符串字母的排列:如何去除重复排列?【英文标题】:Permutation of String letters: How to remove repeated permutations? 【发布时间】:2011-08-02 19:57:41 【问题描述】:这是一个打印字符串字符排列的标准函数:
void permute(char *a, int i, int n)
int j;
if (i == n)
printf("%s\n", a);
else
for (j = i; j < n; j++) //check till end of string
swap((a+i), (a+j));
permute(a, i+1, n);
swap((a+i), (a+j)); //backtrack
void swap (char *x, char *y)
char temp;
temp = *x;
*x = *y;
*y = temp;
它工作正常,但有一个问题,它还会打印一些重复的排列,例如:
如果字符串是“AAB”
输出是:
AAB
ABA
AAB
ABA
BAA
BAA
这也有 3 个重复条目。
有办法防止这种情况发生吗?
--
谢谢
阿洛克克。
【问题讨论】:
正在使用 std::set 不需要的开销? 听起来像是功课。如果是的话,你应该这样标记它。 先生,这不是家庭作业,我只是在研究一些标准算法,我遇到了这个问题。还要感谢 std::set 因为我在 c++ 中不太好所以不知道。 【参考方案1】:记下您之前交换的字符:
char was[256];
/*
for(j = 0; j <= 255; j++)
was[j] = 0;
*/
bzero(was, 256);
for (j = i; j <= n; j++)
if (!was[*(a+j)])
swap((a+i), (a+j));
permute(a, i+1, n);
swap((a+i), (a+j)); //backtrack
was[*(a+j)] = 1;
这必须是迄今为止参赛作品中最快的一个,在“AAAABBBCCD”(100 个循环)上进行了一些基准测试:
native C - real 0m0.547s
STL next_permutation - real 0m2.141s
【讨论】:
你确定你是在比较同类吗?去掉打印后,让 STL 版本就地修改一个 char 数组,你的版本还快吗? 我不会删除打印,因为我不知道编译器会做什么优化。你可以用printf替换stream out,你是对的,这确实有很大的不同。另外,like vs like:我的代码只是 temporary 修改了 char 数组,最后它恢复了原始状态。 @Kumar:当然。你没看到逻辑吗?您的代码给出了重复项,因为它为每个位置使用了所有重复的字符。 我看到了逻辑,但是使用你的这段代码并没有给我正确的结果。我可能做错了什么,但你确定我不需要更改代码的任何其他部分 是否需要第二次交换?【参考方案2】:标准库有你需要的:
#include <algorithm>
#include <iostream>
#include <ostream>
#include <string>
using namespace std;
void print_all_permutations(const string& s)
string s1 = s;
sort(s1.begin(), s1.end());
do
cout << s1 << endl;
while (next_permutation(s1.begin(), s1.end()));
int main()
print_all_permutations("AAB");
结果:
$ ./a.out
AAB
ABA
BAA
【讨论】:
【参考方案3】:另一种方法可能是:
对数组进行预排序。
这将确保所有重复项现在都是连续的。
因此,我们只需要查看我们修复的前一个元素(并置换其他元素)
如果当前元素与前一个元素相同,则不要置换。
【讨论】:
【参考方案4】:我会这样做:首先,我生成字符“组”(即AABBBC
产生两个组:(AA) and (BBB) and (C)
。
首先,我们将AA
的所有分布迭代到n
字符上。对于找到的每个分布,我们将BBB
的所有分布迭代到n-2
剩余字符(未被A
占用)。对于涉及A
s 和B
s 的每个分布,我们将C
的所有分布迭代到剩余的空闲字符位置。
【讨论】:
我真的很喜欢这个,因为你根本不生成重复。 这也是我的想法。但是,实际实现它可能会变得有点麻烦。 不,我认为您甚至可以就地执行此操作,并且非常有效,如果您传递一个空插槽数组(如果您填充一个插槽,则将最后一个数组条目移动到相应的插槽在索引数组中)。【参考方案5】:您可以使用std::set
来确保结果的唯一性。那就是如果它是 C++(因为你这样标记它)。
否则 - 手动检查结果列表并删除重复项。
当然,您必须保存结果并对其进行后处理,而不是像现在这样立即打印。
【讨论】:
谢谢,这会有所帮助,但我希望代码也能在 c 中工作,因为我也用 c 标记了它。所以另一种方式对我来说很好。 @Kumar - 是 C 还是 C++?使用 C 编译器编译时,C++ 代码可能无法工作,C 代码可能无法使用 C++ 编译器。编写 C 代码不会使其成为 C++,请自行决定使用哪种语言。【参考方案6】:如果您只是认为这是一个需要存储所有排列以供将来使用的问题,那将非常简单。
所以你会有一个排列的字符串数组。
现在考虑一个新问题,这也是一个标准问题,您需要从数组中删除重复项。
希望对你有帮助。
【讨论】:
这将创建n!
排列,然后做一个非平凡的过滤器【参考方案7】:
@Kumar,我认为您想要的是以下内容:
#include <stdio.h>
#include <string.h>
/* print all unique permutations of some text. */
void permute(int offset, int* offsets, const char* text, int text_size)
int i;
if (offset < text_size)
char c;
int j;
/* iterate over all possible digit offsets. */
for (i=0; i < text_size; i++)
c=text[i];
/* ignore if an offset further left points to our
location or to the right, with an identical digit.
This avoids duplicates. */
for (j=0; j < offset; j++)
if ((offsets[j] >= i) &&
(text[offsets[j]] == c))
break;
/* nothing found. */
if (j == offset)
/* remember current offset. */
offsets[offset]=i;
/* permute remaining text. */
permute(offset+1, offsets, text, text_size);
else
/* print current permutation. */
for (i=0; i < text_size; i++)
fputc(text[offsets[i]], stdout);
fputc('\n', stdout);
int main(int argc, char* argv[])
int i, offsets[1024];
/* print permutations of all arguments. */
for (i=1; i < argc; i++)
permute(0, offsets, argv[i], strlen(argv[i]));
return 0;
此代码是 C,根据要求,它非常快并且可以执行您想要的操作。当然它包含可能的缓冲区溢出,因为偏移缓冲区具有固定大小,但这只是一个示例,对吧?
编辑:有人试过吗?有没有更简单或更快的解决方案?令人失望的是没有人进一步评论!
【讨论】:
【参考方案8】:void permute(string set, string prefix = "")
if(set.length() == 1)
cout<<"\n"<<prefix<<set;
else
for(int i=0; i<set.length(); i++)
string new_prefix = prefix;
new_prefix.append(&set[i], 1);
string new_set = set;
new_set.erase(i, 1);
permute(new_set, new_prefix);
并简单地将其用作 permute("word");
【讨论】:
【参考方案9】:不要在string
的不同位置置换相同的字符。
在 Python 中:
def unique_permutation(a, l, r):
if l == r:
print ''.join(a)
return
for i in range(l, r+1):
if i != l and a[i] == a[l]:
continue
a[i], a[l] = a[l], a[i]
unique_permutation(a, l+1, r)
a[i], a[l] = a[l], a[i]
【讨论】:
【参考方案10】:算法步骤:
-
将给定的字符串存储到临时字符串中,比如“temp”
从临时字符串中删除重复项
最后调用“void permute(char *a, int i, int n)”函数打印给定字符串的所有排列而不重复
我认为,这是最好和最有效的解决方案。
【讨论】:
这会影响结果的长度,这显然是错误的。以上是关于字符串字母的排列:如何去除重复排列?的主要内容,如果未能解决你的问题,请参考以下文章