LeetCode 31, 91

199 阅读2分钟

LeetCode 31 Next Permutation

链接:leetcode.com/problems/ne…

方法:找规律

时间复杂度:O(n)

空间复杂度:O(1)

想法:这是个经典题了,方法也是比较固定的。首先从后往前找,找到第一个值减小的地方,这时候i-1指向那个比后面的值小的那个值。当然也有可能直接i=0了,这时候就说明数组完全倒序,因此后序直接把数组翻转就完了。但是在i>0的情况下,我们需要从后往前,找到第一个严格大于nums[i-1]的值,然后把这俩元素交换。最后把数组从i到末尾的元素全部倒序。

这个该怎么理解呢?首先如果从前往后找,之后i=0,这个时候直接翻转数组,这是很好理解的。对于i>0的情况,考虑一串数字xxxxxxx2765431,next permutation是找下一个排列,前缀如果能不变的话,就尽可能不要动前缀,因为你改前缀的话得到的数肯定是严格比只改从2之后的数得到的数字更大的。然后,我们从后往前找,找到i-1指向的是2。这个时候因为它后面的都是严格按照单调不增的顺序写在后面的,因此我们就达到了以xxxxxxx2为前缀所能组成的最大的数字。你要是想找下一个排列的话,就肯定这一位不能再以2开头了。那么以谁开头呢?那就是在这后面的严格比2大的第一个数字。我们这里是3。把2和3交换顺序的时候,我们就确认了,下一个排列肯定是以xxxxxxx3开头的,需要找以此为前缀的最小的数。2和3交换顺序后不会影响3后面的数的顺序,后面肯定还是从左往右单调不增的,因此直接把后面再倒一下就完了。

代码:

class Solution {
    public void nextPermutation(int[] nums) {
        int n = nums.length;
        int i = n - 1;
        
        while (i > 0 && nums[i] <= nums[i - 1]) i--;
        if (i > 0) {
            int j = n - 1;
            while (nums[j] <= nums[i - 1]) j--;
            swap(nums, i - 1, j);
        }
        
        swapList(nums, i, n - 1);
    }
    
    private void swap(int[] nums, int l, int r) {
        int tmp = nums[l];
        nums[l] = nums[r];
        nums[r] = tmp;
    }
    
    private void swapList(int[] nums, int l, int r) {
        while (l < r) {
            swap(nums, l, r);
            l++;
            r--;
        }
    }
}

LeetCode 91 Decode Ways

链接:leetcode.com/problems/de…

方法:DP

时间复杂度:O(n)

空间复杂度:O(n)

想法:这道题会发现,字符串的前n-1个元素能够decode出来的方式,与整个字符串能decode出来的方式有关联,因此采用dp。然后就开一个数组表示字符串前n个元素的decode ways是多少。递推公式也比较好想,就是在字符串末尾加入一个元素后,这个元素自己能不能decode出来一个字母(关系到算dp[i]时要不要加上dp[i-1]),然后它能不能跟前面一个元素合起来decode出来一个字母,如果能的话就又能加上dp[i-2]。基本上思路是这样,这个题比较神奇的地方在于dp[0]应该设置为多少。就是说dp[1]代表前1个字符能decode出来几个方式,其实很简单,它如果是0的话就不能解码,如果是1到9的话就有一种解码方式。考虑dp[2]。按照我们的递推公式,假设说前两个元素组成了一个在[10,26]范围的数字,那它是能以这种方式解码出来的。这个时候dp[i]需要加上dp[i-2],这时候两个元素的时候,如果按照两个元素解码,肯定只能有一种解码方式,因为一共就两个元素,因此这个地方dp[0]应该取1。dp[0]=1不是代表什么别的特殊含义,而只是为了让dp[2]的计算正确所采用的办法,你也可以单把dp[1]和dp[2]赋完值之后往下推别的值。

代码:

class Solution {
    public int numDecodings(String s) {
        int n = s.length();
        if (n == 1) return s.charAt(0) == '0' ? 0 : 1;
        
        int[] dp = new int[n + 1];
        dp[0] = 1;
        dp[1] = s.charAt(0) == '0' ? 0 : 1;
        
        for (int i = 2; i <= n; i++) {
            char c = s.charAt(i - 1);
            int num = Integer.parseInt(s.substring(i - 2, i));
            
            if (c == '0') {
                if (num == 10 || num == 20) {
                    dp[i] = dp[i - 2];
                }
                else {
                    return 0;
                }
            }
            else {
                dp[i] += dp[i - 1];
                if (num >= 10 && num <= 26) {
                    dp[i] += dp[i - 2];
                }
            }
        }
        
        return dp[n];
    }
}