劍指offer--字符串全排列/全組合


題目:

輸入一個字符串,打印出該字符串中字符的所有排列。

例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。 

思路:

把一個字符串看成兩部分組成:第一部分為第一個字符,第二部分為后面的所有字符。

求整個字符串的排列,可以看出兩步:首先求所有可能出現在第一個位置的字符,即把第一個字符和后面的所有字符交換;然后固定第一個字符,求后面所有字符的排序。此時仍把后面的字符看成兩部分,第一個字符和后面的字符,然后重復上述步驟。(遞歸)

在后面的在線測試中,要求輸入字符串可能有重復的字符,輸出按照字典順序。

類似題目:

 

 1、輸入一個含有8個數字的數組,判斷有么有可能把這8個數字分別放到正方體的8個頂點上,使得正方體上三組相對的面上的4個頂點的和相等。

 

 思路:相當於求出8個數字的全排列,判斷有沒有一個排列符合題目給定的條件,即三組對面上頂點的和相等。

2、N皇后問題:在8 X 8的國際象棋上擺放八個皇后,使其不能相互攻擊,即任意兩個皇后不得處於同一行,同一列或者同意對角線上,求出所有符合條件的擺法。

思路:由於8個皇后不能處在同一行,那么肯定每個皇后占據一行,這樣可以定義一個數組A[8],數組中第i個數字,即A[i]表示位於第i行的皇后的列號。先把數組A[8]分別用0-7初始化,接下來對該數組做全排列,由於我們用0-7這7個不同的數字初始化數組,因此任意兩個皇后肯定也不同列,那么我們只需要判斷每個排列對應的8個皇后中是否有任意兩個在同一對角線上即可,即對於數組的兩個下標i和j,如果i-j==A[i]-A[j]或i-j==A[j]-A[i],則認為有兩個元素位於了同一個對角線上,則該排列不符合條件。 

//字符串的全排列
//1. 把第一個字符與后面的所有字符進行交換。
//2. 固定第一個字符,將第二個字符與后面字符進行交換。
//3. 重復2操作,第一字符后移,將其交換
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//去掉重復的全排列
//在[nBegin,nEnd)區間中是否有字符與下標為pEnd的字符相等  
bool IsSwap(char* pBegin, char* pEnd)
{
    char *p;
    for (p = pBegin; p < pEnd; p++)
    {
        if (*p == *pEnd)
            return false;
    }
    return true;
}

void sawp(char* s1, char* s2)
{
    char temp = *s1;
    *s1 = *s2;
    *s2 = temp;
}
void Permutation(char* pstr, char* pBegin)
{
    if (*pBegin == '\0') {
        static int num = 0;
        num++;
        cout << "" << num << "個全排列" << pstr << endl;
        //printf("%s\n", pstr);
    }
    else {
        for (char* pCh = pBegin; *pCh != '\0'; ++pCh)
        {
            if (IsSwap(pBegin, pCh)) {
            sawp(pBegin, pCh);
            Permutation(pstr, pBegin + 1);
            sawp(pBegin, pCh);
            }
        }
    }
}

void permutation(char *str)
{
    if (str == NULL)
    {
        return;
    }
    Permutation(str, str);
}

int main()
{
    char a[64];
    int  n;
    cin >> a;
    n = strlen(a) - 1;
    permutation(a);
    system("pause");
    return 0;
}

 

全組合

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

上面我們詳細討論了如何用遞歸的思路求字符串的排列。同樣,本題也可以用遞歸的思路來求字符串的組合。

假設我們想在長度為n的字符串中求m個字符的組合。我們先從頭掃描字符串的第一個字符。針對第一個字符,我們有兩種選擇:第一是把這個字符放到組合中去,接下來我們需要在剩下的n-1個字符中選取m-1個字符;第二是不把這個字符放到組合中去,接下來我們需要在剩下的n-1個字符中選擇m個字符。這兩種選擇都很容易用遞歸實現。下面是這種思路的參考代碼:

//全組合
#include<iostream>  
#include<vector>  
#include<cstring>  
using namespace std;
#include<assert.h>  

void Combination(char *string, int number, vector<char> &result);

void Combination(char *string)
{
    assert(string != NULL);
    vector<char> result;
    int i, length = strlen(string);
    for (i = 1; i <= length; ++i)           //用for循環分開字符組合
        Combination(string, i, result);
}

void Combination(char *string, int number, vector<char> &result)
{
    assert(string != NULL);
    if (number == 0)
    {
        static int num = 1;
        printf("第%d個組合\t", num++);

        vector<char>::iterator iter = result.begin();
        for (; iter != result.end(); ++iter)
            printf("%c", *iter);
        printf("\n");
        return;
    }
    if (*string == '\0')
        return;
    result.push_back(*string);
    Combination(string + 1, number - 1, result);
    result.pop_back();
    Combination(string + 1, number, result);
}

int main(void)
{
    char str[] = "abc";
    Combination(str);
    system("pause");
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM