Chinaunix首页 | 论坛 | 博客
  • 博客访问: 68448
  • 博文数量: 115
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-08 19:09
文章分类
文章存档

2015年(115)

我的朋友

分类: C/C++

2015-08-06 16:43:31

输出一个字符串所有排列。注意有重复字符。
之前写过一个不含重复字符的串的所有排列算法。几经周折,未能想出处理重复的字符的方法。今天发愿研究了半晚上,仍然没能得出正确的解法。最终在stackoverflow上找到一个目前看到的最完美的解法。
 
该解法可作为很多排列组合组合题目的范本,以处理重复元素的情况。根据该范本,对之前的写的算法做了改进,正确处理了重复元素。
 
该算法是基于交换的,对于定的一个串,枚举出所有的两两交换的组合,即可得到所有可能的排列。
这里可以通过回溯来解决。对于一个字符来说,它只和它及它之后的字符交换。
例如abc
可能的交换有组合有
abc: 0<-->0 1<-->1 2<-->2
acb: 0<-->0 1<-->2
bac: 0<-->1 2<-->2
bca: 0<-->1 1<-->2
cab: 0<-->2 1<-->2
cba: 0<-->2 1<-->1
这里可以通过回溯来解决。对于一个字符来说,它只和它及它之后的字符交换。
 
算法的关键在于对重复字符的处理。
引入一个字符数组,用来标记与当前位置的字符交换过的字符
考虑输入abb
1.对于第一个字符a,来说,他可能的交换是自己,第一个b,第二个b
2.显然,在和第一个b交换后,和第二个b就没有必要交换了。否则会产生重复
3.更进一步,如果只还有重复的b,都无需交换。这个判定和与之交换的字符下标无关,仅和其值有关。若输入为abbcb,那么显然,a和第二个b,第三b都无交换的必要。
4.因此,可以通过一个数组,记录下当前,已经与当前处理的位置交换的过的字符。
 
a.'a'和'a'交换 --->记录record['a'] = 1;
b.'a'和'b'交换 --->记录record['b'] = 1;
c.'a'和'b'交换 --->record['b']已经为1,说明交换过,跳过
 

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>

  4. void swap (char *x, char *y)
  5. {
  6.     char temp;
  7.     temp = *x;
  8.     *x = *y;
  9.     *y = temp;
  10. }

  11. void permute(char *a, int i, int n)
  12. {
  13.     char was[256];
  14.     bzero(was, 256);
  15.         int j;
  16.         if (i == n)
  17.              printf("%s\n", a);

  18.     for (j = i; j <= n; j++)
  19.     {
  20.         if (!was[*(a+j)]) {
  21.             swap((a+i), (a+j));
  22.             permute(a, i+1, n);
  23.             swap((a+i), (a+j)); //backtrack
  24.             was[*(a+j)] = 1;
  25.         }
  26.     }

  27. }

  28. int main(){
  29.         char input[] = "aabbcc";
  30.     permute(input,0,strlen(input)-1 );
  31. }

阅读(210) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~