问题是,求一个字符串中字符的所有排列情况。

例如,字符串"abc",用'a',‘b’和'c'能排列的所有字符串
    abc, acb, bac, cab 和 cba

下面有几种代表性的解法,

递归(深度优先搜索)

从集合中依次选出一个元素,作为排列的第一个元素,递归对剩下的元素进行全排列。

固定a,对bc进行排列,得到abc,acb
固定b,得到bac,bca
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void calcAllPermutation(string &s, int from, int to, vector<string> &ans){
    if(to <= 1){ //invalid pos
        return ;
    }else if(from == to){ //valid string found
        ans.push_back(s.substr(0, to + 1));
    }else{
        //permutation for the rest string
        for(int j = from; j <= to; j++){
            swap(s[j], s[from]);
            calcAllPermutation(s, from + 1, to, ans);
            swap(s[j], s[from]);
        }
    }
}

void calcAllPermutationTest(){
    string s = "abc";
    vector<string> ans;
    calcAllPermutation(s, 0, s.size()-1, ans);
    print(ans);
}

测试一下,

abc acb bac bca cba cab 

字典序排列

对于字符,可以定义字典顺序。所以给定两个字符串,逐个字符进行比较,先出现较小字符的串顺序小,字符一直相等,则较短的串顺序小。

对于1~5组成的组合,
    起点:12345
    终点:54321

从起点出发,每次都生成字典序正好比当前排列大的下一个排列

为了使得下一个排列(A)y(B)尽可能小,需要, * A尽可能长 * y尽可能小 * B中的字符由大到小排列

找下一个排列可以用std::next_permutation()

1
2
3
4
5
6
void calcAllPermutation2(string &s, vector<string> &ans){
    do{
        ans.push_back(s);
    }while(next_permutation(s.begin(), s.end()));
    return;
}

测试一下

abc acb bac bca cab cba 

也可以自己写一个,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool calcAllPermutation3Helper(string &s, int num){
    int i = 0;
    //find the pos i of last ascending element
    for(i = num - 2; i >= 0 && s[i] >= s[i+1]; i--){}
    if(i < 0){
        return false;
    }

    int k = 0;
    //find the last pos k greater than s[i]
    for(k = num - 1; k > i && s[k] <= s[i]; k--){}
    swap(s[i], s[k]);
    reverse(s.begin() + i + 1, s.begin() + num); //revers the substr after i
    return true;
}

void calcAllPermutation3(string &s, vector<string> &ans){
    do{
        ans.push_back(s);
    }while(calcAllPermutation3Helper(s, s.size()));
}

测试一下,

abc acb bac bca cab cba