字符串的全組合

字符串的全組合

題目描述:

輸入一個(gè)字符串,輸出該字符串中字符的所有組合。舉個(gè)例子,如果輸入abc,它的組合有a、b、c、ab、ac、bc、abc。

分析和解法:

解法一:遞歸求解

可以考慮求長(zhǎng)度為n的字符串中m個(gè)字符的組合,設(shè)為C(n,m)。原問(wèn)題的解即為C(n, 1), C(n, 2),...C(n, n)的總和。對(duì)于求C(n, m),從第一個(gè)字符開(kāi)始掃描,每個(gè)字符有兩種情況,要么被選中,要么不被選中,如果被選中,遞歸求解C(n-1, m-1)。如果未被選中,遞歸求解C(n-1, m)。不管哪種方式,n的值都會(huì)減少,遞歸的終止條件n=0或m=0。

源代碼如下:

#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

//從一個(gè)字符串中選 m 個(gè)元素 
void Combination_m(char* str, int m, vector<char> &result) 
{
    //字符串為空,或者長(zhǎng)度達(dá)不到 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);
}

//求一個(gè)字符串的組合
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;
}

分析:如果輸入中有重復(fù)字符,上面的程序并不可以去掉重復(fù)的組合。一種處理方法是在事前進(jìn)行一次遍歷,剔除重復(fù)字符,記錄所有不同字符的集合,再傳入Combination()函數(shù)中;還有一種方法是在Combination_m()函數(shù)中加入判斷,判斷當(dāng)前組合是否已經(jīng)存在(即打印)。

解法二:位運(yùn)算

用相應(yīng)位數(shù)的二進(jìn)制數(shù)來(lái)表示一種組合,用 “1” 來(lái)表示取該字符,用 “0” 來(lái)表示不取該字符,依據(jù)二進(jìn)制數(shù)來(lái)輸出相應(yīng)組合。
源代碼如下:

#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的二進(jìn)制中哪些位為 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;
}

分析:這種解法十分巧妙,借助于二進(jìn)制數(shù)來(lái)表示組合,而且可以十分方便快速的知道有多少種組合,即(1 << n) - 1。但是上面的代碼也不能去除重復(fù)的組合。這種解法就只能在事前先篩選出無(wú)重復(fù)的字符在進(jìn)行判斷輸出。

特別注意:

在這里我寫(xiě)一下只有大寫(xiě)字母的字符時(shí)的篩選函數(shù):

char s_str[26];

void Select(char* str, int n)
{
    int j = 0;
    int hash = 0;   //我們用其中的26位來(lái)表示是否含有字符 
    for (int i = 0; i < n; ++i)
    {
        int temp = hash;
        hash |= (1 << (str[i] - 'A'));   //把1左移若干位,1是標(biāo)志位,表示有該字符
        if (temp != hash)
            s_str[j++] = str[i];
    }
}

參考資料:《編程之法》The Art of Programming By July
字符串的全排列和組合算法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1.把二元查找樹(shù)轉(zhuǎn)變成排序的雙向鏈表 題目: 輸入一棵二元查找樹(shù),將該二元查找樹(shù)轉(zhuǎn)換成一個(gè)排序的雙向鏈表。 要求不...
    曲終人散Li閱讀 3,362評(píng)論 0 19
  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問(wèn)題, 分享了一些自己做題目的經(jīng)驗(yàn)。 張土汪:刷leetcod...
    土汪閱讀 12,769評(píng)論 0 33
  • 那深入骨髓 碎了一地的傷痛 不到無(wú)可挽救 不到灰飛煙滅 始終不肯放手 癡情和辜負(fù) 信守和背叛 相互對(duì)立卻又相互依存...
    漁民垂釣閱讀 257評(píng)論 0 0
  • 當(dāng)自己的手敲起鍵盤的時(shí)候,不知自己該如何寫(xiě),聽(tīng)著一首熟悉歌曲,讓我聯(lián)想到了很多,有固執(zhí)、有倔強(qiáng)、有歡笑中帶有悲...
    張氏小萄閱讀 360評(píng)論 0 0
  • Day1 7.18 成都—邛崍—雅安今天一起床,是有點(diǎn)小興奮,但是沒(méi)有想象中那么激動(dòng)。收拾衣服、裝備,調(diào)車,裝...
    柯南Ivy閱讀 281評(píng)論 0 1