• 剑指offer二十七--字符串的排列


    1.题目描述

    1.1题目要求

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

        1.2输入描述

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

    2.解决思路

    2.1递归思想

       2.1.1思路

       先简化问题假设这n个字符不重复
       大问题 n个字符全排列  =》  让n个字符依次做第一个字符,每次固定后  +   后n-1个字符全排列
        举例:abc排列  =》  固定a  bc排列   、  固定b    ac排列  、 固定c  ba排列
        基线条件(递归结束条件): n个字符都排好了
        递归条件(递归继续条件): 排好的字符个数小于n
        递归体:    for(int i = start;i++;i<=n){  1.固定start 2.递归自身排后面的字符}  
         注:固定start,让n个字符依次做第一个字符,通过和第一个字符交换位置来实void Swap(char &a, char &b) {char tmp = a;
        a = b;
        b = tmp;
    }
    
    void PaiLie(char a[], int start, int len, vector<string> &result) {
        if (start == len - 1) {
            result.push_back(a);
            Display(a, len);
            return;
        }
        for (int i = start; i < len; i++) {
            Swap(a[start], a[i]);
            PaiLie(a, start + 1, len, result);
    Swap(a[start], a[i]); // 交换回去因为数组是引用传递,下面的递归交换顺序会影响上一层递归,所以每一层交换要交换回去
    } }   
         然后进一步考虑重复字符的情况,按上面的思路处理,
        例如  abb   =》 1固定a 排bb   、2固定b 排b  ab、3固定  b 排ba
                  aab   =》 1固定a 排ab    2固定a 排 ab    3固定b  排aa 
         可见 问题一:abb    2、3重复,其原因是a与相同元素交换了2次,
                 问题二:aab   1、2重复,原因是a与另一个a交换了,其后n-1个元素相同
    综上,依次让n个字符做第一个字符,不能重复。
    递归体做如下变换:
    for(int i = start;i++;i<=n){  1.只要出现过start位置就不再出现,通过一个set保证 2.递归自身排后面的字符}

    2.1.2递归最终代码

    #include<vector>
    #include<string.h>
    #include <iostream>
    #include<algorithm>
    #include<set>
    using namespace std;
    class Solution {
    public:
      void Swap(char &a, char &b) {
        
        char tmp = a;
        a = b;
        b = tmp;
    }
    
    void PaiLie(char a[], int start, int len, vector<string> &result) {
        if (start == len - 1) {
            result.push_back(a);
            return;
        }
        set<char> is_apper;
        for (int i = start; i < len; i++) {
            if (is_apper.find(a[i]) == is_apper.end()) {// 如果从没有出现start位置,那就去走递归体
                is_apper.insert(a[i]); // 记录a[i]出现过start位置
                Swap(a[start], a[i]);
                PaiLie(a, start + 1, len, result);
                Swap(a[start], a[i]); // 恢复状态
            }
        }
    }
    
    vector<string> Permutation(string str) {
        vector<string> result;
        char *a = new char[str.size()];
        strcpy(a, str.c_str()); // 将string转为char
        PaiLie(a, 0, str.size(), result);
        sort(result.begin(), result.end());// 保证字典序,即result里的东西有序
        return result;
    }
    };

    2.2字典序解法

      2.2.1思路

       abc的答案 abc、acb、bac、bca、cab、cba;知道abc能否推知下一个排序呢?

       字典序法就是这样,按规律推出下一中排列的可能。

      字典序算法的步骤如下:

       1、从原排列中,从右至左,找到第一个左邻小于右邻的字符,记左邻位置为 a。

      2、重新从右至做,找到第一个比 list[a] 大的字符,记为位置为 b。

      3、交换 a 和 b 两个位置的值。

      4、将 a 后面的数,由小到大排列。

    通过找13254的下一个排列理解上步骤

    解释一下:

          步骤一在干嘛:12345下一个是12354,为什么?变得是45交换,不是34交换。因为下一个排列比上一个排列的数值大,且仅大一点。所以我们去先修改个位。从右找到第一个递增相邻。

          步骤二在干嘛:如13254的从右第一个递增相邻是25,所以2要换成其他值 ,那后面有54我们选4不选5为什么呢?因为放在2的位置>2,有点不好说,根据规律得出结论。

          步骤三交换:13254变13452

           步骤四:对于步骤三4位置之后因为刚交换过吗,所以后面应该是最小的排列方式即递增方式

     
     
      
     
  • 相关阅读:
    【线段树】懒标记的维护
    【dp】luoguP4796 关于图 想不到是状压dp (┬_┬)
    【数论】莫比乌斯函数+中国剩余定理
    【积累】Burnside引理和Polya定理
    【排序优化】牛客练习赛54D
    ubuntu修改hostname
    apt-get命令详解
    微信历史版本下载
    vim自动补全快捷键
    Servlet实例
  • 原文地址:https://www.cnblogs.com/linxuesong/p/12171162.html
Copyright © 2020-2023  润新知