LeetCode Top100之31,32题

31. 下一个排列

① 题目描述
  • 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
  • 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
  • 必须原地修改,只允许使用额外常数空间。
  • 以下是一些例子,输入位于左侧列,其相应输出位于右侧列。

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

② 一次遍历
  • 例如 158476531,我们从十位开始,十位右边没有大于 3 的。再看百位,百位右边没有大于 5 的。直到 4 ,右边出现了很多大于 4 的,选那个刚好大于 4 的,也就是 5 。然后交换,变成 158576431,数字变大了,但并不是刚好大于 158476531,我们还需要将 5 右边的数字从小到大排列。变成158513467,就可以结束了。
  • 具体分析过程,参考动图
  • 代码如下:
public void nextPermutation(int[] nums) {
    
    
    int i = nums.length - 2; // 从十位开始找
    //找到第一个不再递增的位置
    while (i >= 0 && nums[i + 1] <= nums[i]) {
    
    
        i--;
    }
    //如果到了最左边,就直接倒置输出
    if (i < 0) {
    
    
        reverse(nums, 0);
        return;
    }
    int j = nums.length - 1;
    //找到刚好大于 nums[i]的位置
    while (nums[j] <= nums[i]) {
    
    
        j--;
    }
    // 交换数字
    swap(nums, i, j);
    //对目标位置的右侧进行倒置
    reverse(nums, i + 1);
}

public void reverse(int[] nums, int start) {
    
    
    int end = nums.length - 1;
    while (start < end) {
    
    
        swap(nums, start, end);
        start++;
        end--;
    }
}

public void swap(int[] nums, int i, int j) {
    
    
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

32. 最长有效括号

① 题目描述
  • 给定一个只包含 '('')' 的字符串,找出最长的包含有效括号的子串的长度。
  • 示例 1:

输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”

  • 示例 2:

输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”

② 暴力法(Time Limit Exceeded
  • 包含优先括号的子串长度肯定为偶数,所以只需要判断长度为2,4,6,…的子串。
  • 判断长度为2的子串,从下标0开始判断,对于每一个下标都只有一种子串,(i,i+1);同时,最后一组子串的下标应该i <= len - curLen
  • 利用辅助方法isValid判断当前子串是否包含有效的括号,如果为truemax < curLen就可以更新max。注意: 这时可以判断长度为curLen += 2的子串了,因为只要求我们找到长度,并没有要求找到所有的子串。
  • 运行报错:Time Limit Exceeded,代码如下:
public int longestValidParentheses(String s) {
    
    
    int len = s.length();
    if (len == 0) {
    
    
        return 0;
    }

    int max = 0;
    for (int curLen = 2; curLen <= len; curLen += 2) {
    
    
        for (int i = 0; i <= len - curLen; i++) {
    
    
            int start = i;
            int end = start + curLen - 1;
            if (isValid(s, start, end) && max < curLen) {
    
    
                max = curLen;
                break;
            }
        }
    }
    return max;
}

public boolean isValid(String s, int start, int end) {
    
    
    int count = 0;
    for (int i = start; i <= end; i++) {
    
    
        if (s.charAt(i) == '(') {
    
    
            count++;
        } else {
    
    
            count--;
        }
        if (count < 0) {
    
    
            return false;
        }
    }
    return count == 0;
}
③ 暴力法优化
  • 解法一中,我们会做很多重复的判断,例如类似于这样的,()()(),从下标 0 开始,我们先判断长度为 2 的是否是合法序列。然后再判断长度是 4 的字符串是否符合,但会从下标 0 开始判断。判断长度为 6 的字符串的时候,依旧从 0 开始,但其实之前已经确认前 4 个已经组成了合法序列,所以我们其实从下标 4 开始判断就可以了。
  • 基于此,我们可以换一个思路,我们判断从每个位置开始的最长合法子串是多长即可。而判断是否是合法子串,我们不用栈,而是用一个变量记录当前的括号情况,遇到左括号加 1,遇到右括号减 1,如果变成 0 ,我们就更新下最长合法子串。
  • 注意: 外层循环i从0开始,执行条件为i < len;内层循环jj = i开始,执行条件为j < leni < lenj = i是为了保证count无需在内层循环之前做更新
  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 代码如下:
public int longestValidParentheses(String s) {
    
    
    int len = s.length();
    if (len == 0) {
    
    
        return 0;
    }
    int max = 0;
    for (int i = 0; i < len; i++) {
    
    
        int count = 0;
        for (int j = i; j < len; j++) {
    
    
            if (s.charAt(j) == '(') {
    
    
                count++;
            } else {
    
    
                count--;
            }
            if (count == 0 && max < (j - i + 1)) {
    
    
                max = j - i + 1;
                continue;
            }
            if (count < 0) {
    
    
                break;
            }
        }
    }
    return max;
}
④ 动态规划
  • dp[i]表示下标以i结尾的最长有效括号。
  • s[i]='('时,dp[i]=0, 不用更新dp[i]的值,让其为初始值0.因为不可能组成有效的括号;
  • s[i]=')'时,有两种情况:
    s[i-1]='('时, dp[i] = dp[i - 2] + 2;
    s[i-1]=')'并且s[i - dp[i - 1] - 1]== '('时,dp[i] = dp[i - dp[i - 1] - 2] + dp[i - 1] + 2
  • 注意:
    ① 直接从下标i = 1开始,因为i=0,无论为何值,都不可能存在有效的括号。
    ② 当s[i-1]='(',更新 dp[i] = dp[i - 2] + 2时,一定要判断i - 2 >= 0。因为可能是()(())()的情况。
    ③ 当s[i-1]=')',要判断s[i - dp[i - 1] - 1]== '('时,一定要先判断i - dp[i - 1] - 1 >= 0。因为可能是())的情况。
    ④ 当s[i-1]=')'并且s[i - dp[i - 1] - 1]== '('时,更新dp[i] = dp[i - dp[i - 1] - 2] + dp[i - 1] + 2时,一定要判断i - dp[i - 1] - 2 >= 0)。因为可能是(())的情况。
  • 代码如下:
public int longestValidParentheses(String s) {
    
    
    int len = s.length();
    if (len == 0) {
    
    
        return 0;
    }
    int max = 0;
    int[] dp = new int[len];
    for (int i = 1; i < len; i++) {
    
    
        if (s.charAt(i) == ')') {
    
    
            if (s.charAt(i - 1) == '(') {
    
    
                dp[i] = (i - 2 >= 0) ? dp[i - 2] + 2 : 2;
            } else {
    
    
                if (i - dp[i - 1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
    
    
                    dp[i] = (i - dp[i - 1] - 2 >= 0) ? dp[i - dp[i - 1] - 2] + dp[i - 1] + 2 : dp[i - 1] + 2;
                }
            }
            max = Math.max(dp[i], max);
        }
    }
    return max;
}

猜你喜欢

转载自blog.csdn.net/u014454538/article/details/90474970