算法问题:排列

下一个排列

题目31. Next Permutation

:策略:查找最长倒序的后缀,然后将这个后缀与前面的数字交换,翻转过来,就是下一个序列。

class Solution {
    public void nextPermutation(int[] nums) {
        // find i such that nums[i]<nums[i+1] from nums.length-1 to 0
        // if i does not exist,just reverse the array
        int i = nums.length-2;
        while(i>=0 && nums[i]>=nums[i+1])--i;
        
        if(i>=0){
            // find a position for i to swap
            // i.e. find the rightmost smallest e in [i+1,n-1] such that e>i
            int k=rightmost(nums,nums[i],i+1,nums.length-1);
            swap(nums,i,k);
        }
        reverse(nums,i+1,nums.length-1);
    }
    int rightmost(int[] nums,int e,int i,int j){
        while(i<j){
            int m =j+(i-j)/2;
            if(nums[m]>e)i=m;
            else j=m-1;
        }
        return i;
    }
    
    void reverse(int[] nums,int i,int j){
        for(int r=i,p=j;r<p;++r,--p){
            int t = nums[r];
            nums[r] = nums[p];
            nums[p] = t;
        }
    }
    void swap(int[] nums,int i,int j){
        int t=nums[i];
        nums[i]=nums[j];
        nums[j]=t;
    }
}

算法解释:因为一个排列中可能存在重复,即1 1 3 3 2[4] 2[5], 如果只是将2[4]和2[5]进行交换,并不能产生下一个排列。只要找到下一个逆序对,即A[i]>A[i+1]的点,交换它们,即可得到下一个排列。为什么?因为从末尾往前查找,所以它们一定是最小的那个。
然后,还需要额外的一步:将尾部进行翻转,因为i+1之后全都是逆序的,翻转过来就是最小的。

总而言之,算法分为两个步骤: 1.找到逆序对,交互 2.翻转尾部

可枚举的排列选择

题目1220. Count Vowels Permutation

:使用滚动数组+常量状态优化。

class Solution {
    private final int mod = (int)Math.pow(10, 9) + 7;
    public int countVowelPermutation(int n) {
        if(n == 1) {
            return 5;
        }
        // dp[i][j] simplified to 5 constants using a rolling strategy.
        long a = 1, e = 1, i = 1, o = 1, u = 1;
        for(int m = 2; m <= n; m++) {
            long newA = i + u + e;
            long newE = a + i;
            long newI = o + e;
            long newU = o + i;
            long newO = i;
            a = newA % mod;
            e = newE % mod;
            i = newI % mod;
            u = newU % mod;
            o = newO % mod;
        }
        return (int)((a + e + i + o + u) % mod);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值