字串全排列

xfcoding發表於2024-06-04

連結:https://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7
來源:牛客網

輸入一個長度為 n 字串,列印出該字串中字元的所有排列,你可以以任意順序返回這個字串陣列。
例如輸入字串ABC,則輸出由字元A,B,C所能排列出來的所有字串ABC,ACB,BAC,BCA,CBA和CAB。

/**
     * 1、遞迴演算法
     *
     * 解析:http://www.cnblogs.com/cxjchen/p/3932949.html  (感謝該文作者!)
     *
     * 對於無重複值的情況
     *
     * 固定第一個字元,遞迴取得首位後面的各種字串組合;
     * 再把第一個字元與後面每一個字元交換,並同樣遞迴獲得首位後面的字串組合; *遞迴的出口,就是隻剩一個字元的時候,遞迴的迴圈過程,就是從每個子串的第二個字元開始依次與第一個字元交換,然後繼續處理子串。
     *
     * 假如有重複值呢?
     * *由於全排列就是從第一個數字起,每個數分別與它後面的數字交換,我們先嚐試加個這樣的判斷——如果一個數與後面的數字相同那麼這兩個數就不交換了。
     * 例如abb,第一個數與後面兩個數交換得bab,bba。然後abb中第二個數和第三個數相同,就不用交換了。
     * 但是對bab,第二個數和第三個數不 同,則需要交換,得到bba。
     * 由於這裡的bba和開始第一個數與第三個數交換的結果相同了,因此這個方法不行。
     *
     * 換種思維,對abb,第一個數a與第二個數b交換得到bab,然後考慮第一個數與第三個數交換,此時由於第三個數等於第二個數,
     * 所以第一個數就不再用與第三個數交換了。再考慮bab,它的第二個數與第三個數交換可以解決bba。此時全排列生成完畢!
     *
     *
     * @param str
     * @return
     */
 
public ArrayList<String> Permutation(String str){
 
        ArrayList<String> list = new ArrayList<String>();
        if(str!=null && str.length()>0){
            PermutationHelper(str.toCharArray(),0,list);
            Collections.sort(list);
        }
        return list;
    }
    private void PermutationHelper(char[] chars,int i,ArrayList<String> list){
        if(i == chars.length-1){
            list.add(String.valueOf(chars));
        }else{
            Set<Character> charSet = new HashSet<Character>();
            for(int j=i;j<chars.length;++j){
                if(j==i || !charSet.contains(chars[j])){
                    charSet.add(chars[j]);
                    swap(chars,i,j);
                    PermutationHelper(chars,i+1,list);
                    swap(chars,j,i);
                }
            }
        }
    }
 
    private void swap(char[] cs,int i,int j){
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
 
/**
     * 2、字典序排列演算法
     *
     * 可參考解析: http://www.cnblogs.com/pmars/archive/2013/12/04/3458289.html  (感謝作者)
     *
     * 一個全排列可看做一個字串,字串可有字首、字尾。
     * 生成給定全排列的下一個排列.所謂一個的下一個就是這一個與下一個之間沒有其他的。
     * 這就要求這一個與下一個有儘可能長的共同字首,也即變化限制在儘可能短的字尾上。
     *
     * [例]839647521是1--9的排列。1—9的排列最前面的是123456789,最後面的987654321,
     * 從右向左掃描若都是增的,就到了987654321,也就沒有下一個了。否則找出第一次出現下降的位置。
     *
     * 【例】 如何得到346987521的下一個
     * 1,從尾部往前找第一個P(i-1) < P(i)的位置
     * 3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1
     * 最終找到6是第一個變小的數字,記錄下6的位置i-1
     *
     * 2,從i位置往後找到最後一個大於6的數
     * 3 4 6 -> 9 -> 8 -> 7 5 2 1
     * 最終找到7的位置,記錄位置為m
     *
     * 3,交換位置i-1和m的值
     * 3 4 7 9 8 6 5 2 1
     * 4,倒序i位置後的所有資料
     * 3 4 7 1 2 5 6 8 9
     * 則347125689為346987521的下一個排列
     *
     * @param str
     * @return
     */
 
public ArrayList<String> Permutation2(String str){
        ArrayList<String> list = new ArrayList<String>();
        if(str==null || str.length()==0){
            return list;
        }
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        list.add(String.valueOf(chars));
        int len = chars.length;
        while(true){
            int lIndex = len-1;
            int rIndex;
            while(lIndex>=1 && chars[lIndex-1]>=chars[lIndex]){
                lIndex--;
            }
            if(lIndex == 0)
                break;
            rIndex = lIndex;
            while(rIndex<len && chars[rIndex]>chars[lIndex-1]){
                rIndex++;
            }
            swap(chars,lIndex-1,rIndex-1);
            reverse(chars,lIndex);
 
            list.add(String.valueOf(chars));
        }
 
        return list;
    }
 
    private void reverse(char[] chars,int k){
        if(chars==null || chars.length<=k)
            return;
        int len = chars.length;
        for(int i=0;i<(len-k)/2;i++){
            int m = k+i;
            int n = len-1-i;
            if(m<=n){
                swap(chars,m,n);
            }
        }
 
    }

相關文章