1. 程式人生 > >lintcode10- Permutation Index II- medium

lintcode10- Permutation Index II- medium

次數 下一個 也不能 重復數 一個 組成 example con which

Given a permutation which may contain repeated numbers, find its index in all the permutations of these numbers, which are ordered in lexicographical order. The index begins at 1.

Example

Given the permutation [1, 4, 2, 2], return 3.

算法:和前面一題都是事先數學想法。有重復時,每一個位idx加和的公式是:貢獻 = 後面小的數的個數 * 後面數字個數 / 能用來排列的數字裏重復次數的階乘M。 M 比較復雜,它跟具體後面哪個數字被換到前面來有關,因為不同數被換到這一位後,後面能用來排列的數不一樣,具體組成是,後面本來的數 - 被換到前面的數 + 換到後面去的數。

看一看例子 3,5,5,6,7,3,2,1,1 。比如要算第一個‘3’位上的貢獻,其實就是看後面曾經換到這個位置上的數,產生過多少種排列。因為有重復數字,問題沒有那麽簡單,每個數字換到第一位後,剩余數字還有沒有重復,能產生多少種排列的情況就都不相同了,所以一定要二重循環遍歷每種後面的小的數字換到這個位置產生排列的可能。 具體比如說把後面的‘1’ 換到第一個位置來,‘3’這時候被換到後面去了,那這個‘1’產生的貢獻就是用“3,5,5,6,7,3,2,3,1”這麽多數字能產生多少種排列。其實就是全排列/ 重復次數階乘的積,除掉階乘積是因為你要保證5,5你只算5a,5b有效,如果有3個6,那你只算6a,6b,6c有效,這樣就能去重了,階乘表示的就是這幾個小數字內部的全排列,積表示的就是可能有好幾套重復,55,666。

細節:不能把後面兩個1都換到前面去,這裏是另一個去重,用set避免一下。

public class Solution {
    /**
     * @param A an integer array
     * @return a long integer
     */
    long fac(int numerator) {
        long now = 1;
        for (int i = 1; i <= numerator; i++) {
            now *= (long) i;
        }
        return now;
    }   
    
    
// 返回 後面多少數! / 後面重復數階乘的積 long generateNum(HashMap<Integer, Integer> hash) { long denominator = 1; int sum = 0; for (int val : hash.values()) { if(val == 0 ) continue; denominator *= fac(val); sum += val; } if(sum==0) { return sum; } return fac(sum) / denominator; } public long permutationIndexII(int[] A) { HashMap<Integer, Integer> hash = new HashMap<Integer, Integer>(); for (int i = 0; i < A.length; i++) { if (hash.containsKey(A[i])) hash.put(A[i], hash.get(A[i]) + 1); else { hash.put(A[i], 1); } } long ans = 0; for (int i = 0; i < A.length; i++) { HashMap<Integer, Integer> flag = new HashMap<Integer, Integer>(); //遍歷每一個i後面的數字,如果小而且沒有被換到這位過的話,試著換換看算它換到這位後,後面數字的排列方法。 for (int j = i + 1; j < A.length; j++) { if (A[j] < A[i] && !flag.containsKey(A[j])) { // 標記一下,下次不要再選這個重復數[3,5,5,6,7,2,1,1] 不要兩個1都換到前面試 flag.put(A[j], 1); // 自己被換到前面了,等會不參與後面數字的排列,去掉 hash.put(A[j], hash.get(A[j])-1); // 算出後面多少數! / 後面重復數階乘的積 ans += generateNum(hash); // 把修改變回來,為下一個小數換上來做準備 hash.put(A[j], hash.get(A[j])+1); } } // 以後數字i用後面的數字算全排列再也不能用這一位了,只能往後看 hash.put(A[i], hash.get(A[i])-1); } return ans + 1; } }

lintcode10- Permutation Index II- medium