字符串的全組合
題目描述:
輸入一個字符串,輸出該字符串中字符的所有組合。舉個例子,如果輸入abc,它的組合有a、b、c、ab、ac、bc、abc。
分析和解法:
解法一:遞歸求解
可以考慮求長度為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。
源代碼如下:
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
//從一個字符串中選 m 個元素
void Combination_m(char* str, int m, vector<char> &result)
{
//字符串為空,或者長度達不到 m
if (str == NULL || (*str == '\0' && m != 0))
return;
//出口,輸出組合
if(m == 0)
{
static int count = 0;
cout << ++count << ":";
for (int i = 0; i < result.size(); i++)
cout << result[i];
cout << endl;
return;
}
result.push_back(*str);
Combination_m(str+1, m-1, result);
result.pop_back();
Combination_m(str+1, m, result);
}
//求一個字符串的組合
void Combination(char* str)
{
if (str == NULL || *str == '\0')
return;
int num = strlen(str);
for (int i = 1; i <= num; i++)
{
vector<char> result;
Combination_m(str, i, result);
}
}
int main()
{
char str[20];
cin >> str;
Combination(str);
return 0;
}
分析:如果輸入中有重復字符,上面的程序并不可以去掉重復的組合。一種處理方法是在事前進行一次遍歷,剔除重復字符,記錄所有不同字符的集合,再傳入Combination()函數中;還有一種方法是在Combination_m()函數中加入判斷,判斷當前組合是否已經存在(即打印)。
解法二:位運算
用相應位數的二進制數來表示一種組合,用 “1” 來表示取該字符,用 “0” 來表示不取該字符,依據二進制數來輸出相應組合。
源代碼如下:
#include <iostream>
#include <cstring>
using namespace std;
void print_subset(char* str, int n, int s)
{
static int count = 0;
cout << ++count << ":";
for (int i = 0; i < n; i++)
{
if (s & (1 << i)) //判斷s的二進制中哪些位為 1,就取哪些位
cout << str[i];
}
cout << endl;
}
void subset(char* str, int n)
{
for (int i = 1; i < (1 << n); i++)
print_subset(str, n, i);
}
int main()
{
char str[20];
cin >> str;
subset(str, strlen(str));
return 0;
}
分析:這種解法十分巧妙,借助于二進制數來表示組合,而且可以十分方便快速的知道有多少種組合,即(1 << n) - 1。但是上面的代碼也不能去除重復的組合。這種解法就只能在事前先篩選出無重復的字符在進行判斷輸出。
特別注意:
在這里我寫一下只有大寫字母的字符時的篩選函數:
char s_str[26];
void Select(char* str, int n)
{
int j = 0;
int hash = 0; //我們用其中的26位來表示是否含有字符
for (int i = 0; i < n; ++i)
{
int temp = hash;
hash |= (1 << (str[i] - 'A')); //把1左移若干位,1是標志位,表示有該字符
if (temp != hash)
s_str[j++] = str[i];
}
}
參考資料:《編程之法》The Art of Programming By July
字符串的全排列和組合算法