55. 2种方法求字符串的组合[string combination]

【本文链接】

http://www.cnblogs.com/hellogiser/p/string-combination.html

题目】

题目:输入一个字符串,输出该字符串中字符的所有组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。

分析

在之前的博文28.字符串的排列[StringPermutation]中讨论了如何用递归的思路求字符串的排列。同样,本题也可以用递归的思路来求字符串的组合。


 【递归法求组合】

可以考虑求长度为n的字符串中m个字符的组合,设为C(n,m)。原问题的解即为C(n, 1), C(n, 2),...C(n, n)的总和。对于求C(n, m),从第一个字符开始扫描,每个字符有两种情况,要么被选中,要么不被选中。如果被选中,递归求解C(n-1, m-1);如果未被选中,递归求解C(n-1, m)。不管哪种方式,n的值都会减少,递归的终止条件n=0或m=0。

【代码】

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 
// 55_StringCombination.cpp : Defines the entry point for the console application.
//
/*
    version: 1.0
    author: hellogiser
    blog: http://www.cnblogs.com/hellogiser
    date: 2014/5/24
*/


#include "stdafx.h"
#include <vector>
#include <iostream>
using namespace std;

// print string combination
void Print(vector<char> &result)
{
    vector<
char>::const_iterator iterBegin = result.begin();
    vector<
char>::const_iterator iterEnd = result.end();

    
for (; iterBegin != iterEnd; ++ iterBegin)
        printf(
"%c", *iterBegin);
    printf(
"\n");
}

// get string combination recursively
// choose m chars from str
void Combination(char *str, unsigned int m, vector<char> &result)
{
    
if(NULL == str || (*str == '\0' && m > 0))
        
return;

    
// base cases
    if (m == 0)
    {
        
// we have got a combination,print it
        Print(result);
        
return;
    }
    
// (1)choose current char
    result.push_back(*str);
    
// choose m-1 chars from remaining n-1 chars
    Combination(str + 1, m - 1, result);

    
// (2) not choose current char
    result.pop_back();
    
// choose m chars from remaining n-1 chars
    Combination(str + 1, m, result);
}

// string combination
void StringCombination(char *str)
{
    
if(NULL == str || *str == '\0')
        
return;
    
int len = strlen(str);
    vector<
char> result;
    
for (int i = 1; i <= len; ++i)
        Combination(str, i, result);
}

void test_base(char *str)
{
    StringCombination(str);
    printf(
"---------------------\n");
}

void test_case1()
{
    
char str[] = "";
    test_base(str);
}

void test_case2()
{
    
char str[] = "a";
    test_base(str);
}

void test_case3()
{
    
char str[] = "abc";
    test_base(str);
}

void test_main()
{
    test_case1();
    test_case2();
    test_case3();
}

int _tmain(int argc, _TCHAR *argv[])
{
    test_main();
    
return 0;
}
/*
---------------------
a
---------------------
a
b
c
ab
ac
bc
abc
---------------------
*/

由于组合可以是1个字符的组合,2个字符的组合……一直到n个字符的组合,因此在函数void StringCombination(char *str)中,需要一个for循环。另外,用一个vector来存放选择放进组合里的字符。


 【位运算求组合】

另外本题还有一个巧妙的思路,可以从位运算出发求组合。用一个二进制数字,来决定字符的取舍,某一位为1,则取对应的字符,若为0则不取,就能够实现字符组合。

例如对于“abc”,长度为3,则共有7种组合可能。让num 从1自增到7,跟字符的每一位进行判断,是否取舍。

比如:num=1,即001时:

(1)j指向第1个字符,(a>>j)&1==1,则取a;

(2)j指向第2个字符,(a>>j)&1==0,则舍弃b;

(3)j指向第3个字符,(a>>j)&1==0,则舍弃c;

此次组合的字符串为a;

以此类推。

当num=7,即111时:

(1)j指向第1个字符,(a>>j)&1==1,则取a;

(2)j指向第2个字符,(a>>j)&1==1,则取b;

(3)j指向第3个字符,(a>>j)&1==1,则取c;

此次组合的字符串为abc;

那么当num依次取完所有的值,就可以得到所有的字符串组合。

【代码】

 

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
/*
    version: 1.0
    author: hellogiser
    blog: http://www.cnblogs.com/hellogiser
    date: 2014/5/24
*/

void StringCombinationUsingBitwise(char *str)
{
    
// use bitwise operations to get string combination
    if(NULL == str || *str == '\0')
        
return;
    
int len = strlen(str);
    
if (len >= 32)
        
return;
    
int sum = 1 << len;
    
for (int i = 1; i < sum; ++i)
    {
        
for (int j = 0; j < len; j++)
        {
            
if ((i >> j) & 0x1)
            {
                
// choose char at str[j]
                printf("%c", str[j]);
            }
        }
        printf(
"\n");
    }
}

相对于 【递归法求组合】【位运算求组合】速度更快,其时间复杂度为T=n*2n,但是n不能超过32.

【注意】

多谢“路上的脚印”的提醒,该算法只能适用于字符串中字符都不相同的情形。如果有相同字符,则不再适合,需要进一步修正。

【参考】

http://zhedahht.blog.163.com/blog/static/2541117420114172812217/

http://zhuyanfeng.com/archives/3246

http://blog.csdn.net/hackbuteer1/article/details/7462447

http://blog.csdn.net/wuzhekai1985/article/details/6643127

 【本文链接】

http://www.cnblogs.com/hellogiser/p/string-combination.html