【算法基础】字符串的全排列算法

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

这道题是剑指offfer中一道经典的题,如果有n个元素,我们就能有n*(n - 1)个全排列,n为1时,只有一个全排列

举个栗子,当字符串为ABC时,分析一下全排列的过程:

1.固定A在字符串的最左边,然后全排列B和C

2.全排列B和C

3.当全排列的字符串只有一个字符时,它的全排列就是它自身

4.当固定A后,其他字符串的全排列完毕后,把A与其他字符进行交换,然后重复上面的步骤进行全排列

示意图如下:

基于这种思路,我使用回溯法解决,代码如下:

class Solution {
public:
    vector<string> Permutation(string str) {
        vector<string> ret;
        if (str.empty()) {
            return ret;
        }
        string tmp = "";
        _permutation(str, ret, tmp, 0);
        return ret;
    }
private:
    void _permutation(string str, vector<string>& ret, string& tmp, int begin) {
        if (begin == str.size()) {
            ret.push_back(tmp);
            return;
        }
        for (int i = begin; i < str.size(); ++i) {
            if (i != begin && str[i] == str[begin])
                continue;
            swap(str[i], str[begin]);
            tmp += str[begin];
            _permutation(str, ret, tmp, begin + 1);
            tmp.pop_back();
        }
    }
};

这里主要解释一下这个函数:

void _permutation(string str, vector<string>& ret, string& tmp, int begin) {
        if (begin == str.size()) {
            ret.push_back(tmp);
            return;
        }
        for (int i = begin; i < str.size(); ++i) {
            if (i != begin && str[i] == str[begin])
                continue;
            swap(str[i], str[begin]);
            tmp += str[begin];
            _permutation(str, ret, tmp, begin + 1);
            tmp.pop_back();
        }
    }

这个函数的思路就是上面的思路,使用回溯法,思路如下

1. 让当前字符去和每个字符串交换

2.交换完成后,递归的调用函数去处理除当前字符之外剩下的字符

递归的出口就是begin等于str.size(),这时候的tmp(因为一直在保存每一个字符),就是一个全排列的结果,保存在数组ret之中

注意,在循环中有这么一个判断:

if (i != begin && str[i] == str[begin])
    continue;

这个判断主要是处理字符串中有重复字符的

去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换

在这个循环遍历时,有两种情况str[i]和str[begin]相等,一种是i和begin相等,即同一个元素,一种是相同的元素

这时候如果交换的话没有意义,就不用交换并且保存了,不然就会出现重复的结果

举个栗子,当字符串为aabb时,如果注释掉这句话,ret的结果是

如果不注释掉,结果是正确的:

如果要按照字典序输出的话,还需要对数组ret进行排序

再介绍一种简单的方法,如果为了快速AC的话,可以使用STL中的next_permutation函数,函数定义如下:

template <class BidirectionalIterator>
  bool next_permutation (BidirectionalIterator first,
                         BidirectionalIterator last);

template <class BidirectionalIterator, class Compare>
  bool next_permutation (BidirectionalIterator first,
                         BidirectionalIterator last, Compare comp);

解决的代码如下:

vector<string> Permutation(string str) {
        vector<string> answer;
        if(str.empty())
            return answer;       
        sort(str.begin(),str.end());
        do{
            answer.push_back(str);
        }
        while(next_permutation(str.begin(),str.end()));
        return answer;
}

这个函数的文档链接如下:

http://www.cplusplus.com/reference/algorithm/next_permutation/?kw=next_permutation
---------------------  
作者:Qregi  
来源:CSDN  
原文:https://blog.csdn.net/Qregi/article/details/82049298  
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/wojiuguowei/article/details/84023194