Chinaunix首页 | 论坛 | 博客
  • 博客访问: 986558
  • 博文数量: 150
  • 博客积分: 3017
  • 博客等级: 少校
  • 技术积分: 3829
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-19 14:40
个人简介

Now in Baidu WISE team

文章分类

全部博文(150)

文章存档

2014年(8)

2013年(31)

2012年(111)

分类: C/C++

2013-01-02 23:29: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. }

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