字符串的排列

问题描述:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
示例
输入:"ab"
返回值:["ab","ba"]

方法一

思路分析

本题我们能想到的最直接的办法就是利用数学中的全排列,即对于一个字符串,首先确定第一个位置的字符,使用swap交换函数依次从当前位置的字符开始遍历寻找其余字符,从而得到第一个位置字符确定的字符串,而后其余位的字符也是用相同的办法,当确定到最后一个字符时,直接将最后一个字符插入到字符串中,递归结束,输出所有的排列。

图解

输入字符串“abc”,通过递归算法输出其全排列,通过第一次swap操作得到第一个位置上的确定字符,共三种情况,而后对剩余位的确定采用同样的办法。


C++核心代码

class Solution {
public:
    vector<string> Permutation(string str) {
        if(str.empty())
            return {};
        set<string> tmp;
        perm(0, str, tmp);//从第一个位置开始递归
        return vector<string>({tmp.begin(), tmp.end()});
    }
    void perm(int pos, string s, set<string> &ret)
    {
        if (pos+1 == s.length())//运行到最后一个字符结束
        {
            ret.insert(s);
            return;
        }
        for (int i = pos; i < s.length(); ++i)
        {
            swap(s[pos], s[i]);//每次将当前位置的字符与后面的所有字符交换而形成不同的可能
            perm(pos+1, s, ret);//相同方法排列剩余的位
            swap(s[pos], s[i]);//将字符串归位
        }
            
    }
    
};

复杂度分析

  • 时间复杂度:字符串全排列的组合的时间为$O(n!)$,因此时间复杂度为$O(n!)$
  • 空间复杂度:  递归算法的空间复杂度为递归的深度*每次递归创建变量的个数,辅助数组tmp要放入的元素有$1+2+3+...+n=n(n-1)/2$,因此时间复杂度为$O(n^2)$

方法二

思路分析

  本题可以采用字典序的方法,字典序就是说给定两个字符串,逐个字符比较,那么先出现较小字符的那个串字典顺序小,例如abc>acb,那么本题可以先将给定的字符串按照字典序的顺序排序,形成最大字典序的字符串,例如给定字符串“abdc”,则其最大字典序的字符串为“abcd”,接着寻找下一个排列组合,为“abdc”,直到下一个排列组合为空,运行结束。接下来将介绍寻找下一个排列组合算法的思想步骤。
  • 首先将字符串按升序排序,找到字典序最大的字符串
  • 从字符串数组尾部开始从右往左寻找两个相邻元素(必须是相邻的),左边元素为arr[i],右边元素为arr[i+1],并且arr[i]的字典序大于arr[i+1]
  • 之后再从尾端开始往前检验,找出第一个字典序小于arr[i]的元素arr[j],交换两个元素
  • 将arr[i]之后的所有元素颠倒排序

实例分析

给定字符串“abc”,首先按升序方式找到最大的字符串,图中对调表示尾端开始往前检验,找出第一个字典序小于arr[i]的元素arr[j],交换两个元素;重排表示arr[i]之后的所有元素颠倒排序;在此过程中将所有的排列输出


python核心代码

# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        # write code here
        n = len(ss)
        arr = list(sorted(ss))
        test = list(reversed(arr))
        ans = []
        # 生成下一个排列
        while arr != test:
            ans.append(''.join(arr))
            i = n - 2
            while i > 0 and arr[i] >= arr[i+1]:#从尾部开始往前检验,检验相邻位置的字典序大小
                i -= 1
            if i<0:return 
            j = n - 1
            while j > i-1 and arr[j] <= arr[i]:#找出第一个字典序小于arr[i]的元素arr[j]
                j -= 1
            m=arr[i]
            arr[i]=arr[j]
            arr[j]=m#交换两个元素元素
            arr = arr[:i+1] + sorted(arr[i+1:])#arr[i]之后的所有元素颠倒排序
        ans.append(''.join(test))#在此过程中将所有的排列输出
        return ans

复杂度分析

  • 时间复杂度:字符串全排列的时间复杂度为$O(n!)$,因此时间复杂度为$O(n!)$
  • 空间复杂度:借助于一个辅助数组存储字符串,空间复杂度为$O(n)$