lintcode10- Permutation Index II- medium

Posted jasminemzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lintcode10- Permutation Index II- medium相关的知识,希望对你有一定的参考价值。

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的主要内容,如果未能解决你的问题,请参考以下文章

lintcode10- String Permutation II- medium

lintcode-medium-Previous Permutation

lintcode-medium-Next Permutation II

lintcode-medium-Permutation Index II

lintcode-medium-Permutation Sequence

[Lintcode]52. Next Permutation