内卷大厂系列《括号问题四连击》

557 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

大厂高频算法面试题:《括号问题系列》,通过括号问题四连击,您将学到通过有限变量解法,如何定义动态规划含义。

一、括号问题I

括号有效配对是指:

1)任何一个左括号都能找到和其正确配对的右括号

2)任何一个右括号都能找到和其正确配对的左括号

有效的: (()) ()() (()()) 等

无效的: (() )( 等

怎么判断一个括号字符串有效?

1、分析

只需要一个变量即可搞定,有效括号配对问题,遇到左括号,count++,遇到右括号,count--,如果count小于0,说明右括号比左括号多,后续不用看了,直接返回false,如果遍历结束,count == 0,说明是有效,否则无效

2、实现

public static boolean valid(String s) {
    if (s == null || s.length() == 0) {
        return false;
    }
    char[] str = s.toCharArray();
    int count = 0;
    for (int i = 0; i < str.length; i++) {
        count += str[i] == '(' ? 1 : -1;
        if (count < 0) {
            return false;
        }
    }
    return count == 0;
}

二、括号问题II

括号有效配对是指:

1)任何一个左括号都能找到和其正确配对的右括号

2)任何一个右括号都能找到和其正确配对的左括号

有效的: (()) ()() (()()) 等

无效的: (() )( 等

如果一个括号字符串无效,返回至少填几个字符能让其整体有效

1、分析

只需要两个变量即可搞定,count(含义跟括号问题I一样),need,如果count == -1,说明右括号比左括号多一个,此时need++,count恢复到0,count = 0,如果遍历完毕后,发现count>0,说明左括号比右括号大,需要补偿count次右括号,所以至少需要添加need + count,如果count == 0,则至少需要添加need

2、实现

public static int needParentheses(String s) {
    if (s == null || s.length() == 0) {
        return 0;
    }
    char[] str = s.toCharArray();
    int count = 0;
    int need = 0;
    for (int i = 0; i < str.length; i++) {
        if (str[i] == '(') {
            count++;
        } else { // 遇到的是')'
            if (count == 0) {
                need++;
            } else {
                count--;
            }
        }
    }
    return count + need;
}

三、括号问题III

括号有效配对是指:

1)任何一个左括号都能找到和其正确配对的右括号

2)任何一个右括号都能找到和其正确配对的左括号

返回一个有效括号字符串中,最大嵌套层数

比如:(()()) 返回2层,(()(())) 返回3层,()(()) 返回2层

1、分析

遇到左括号count++,遇到右括号count--,count最大值就是嵌套了几层

2、实现

// 校验是否是合法有效括号
public static boolean isValid(char[] str) {
    if (str == null || str.length == 0) {
        return false;
    }
    int status = 0;
    for (int i = 0; i < str.length; i++) {
        if (str[i] != ')' && str[i] != '(') {
            return false;
        }
        if (str[i] == ')' && --status < 0) {
            return false;
        }
        if (str[i] == '(') {
            status++;
        }
    }
    return status == 0;
}

public static int deep(String s) {
    char[] str = s.toCharArray();
    if (!isValid(str)) {
        return 0;
    }
    int count = 0;
    int max = 0;
    for (int i = 0; i < str.length; i++) {
        if (str[i] == '(') {
            max = Math.max(max, ++count);
        } else {
            count--;
        }
    }
    return max;
}

四、括号问题IV

括号有效配对是指:

1)任何一个左括号都能找到和其正确配对的右括号

2)任何一个右括号都能找到和其正确配对的左括号

返回一个括号字符串中,最长的括号有效子串的长度

1、分析

子串一定是连续的

以什么什么结尾入手

dp[i]含义:arr[0...i]中以i结尾的最长有效括号子串长度是多少,潜台词:如果i遇到左括号,则dp[i]等于0,因为不可能有效括号以左括号结尾,违背dp[i]定义的含义

如果当前来到i位置是右括号,i-1位置的dp值为4,说明dp[i-1]往前推,能推动4个合法的括号,那么i-5位置上如果是左括号,那么至少dp[i] = 5,如果i-5位置上是右括号,那么dp[i] = 0,就算左边还有遇到左括号能匹配上,也是0,因为dp[i]的含义就是能匹配的有效括号子串最长长度,dp[i-1]已经记录了最长的括号有效子串的长度,前边就算有左括号匹配,也是dp[i-1]算好的值

  • i位置是右括号,如果dp[i-1]的前一位是右括号,则dp[i] = 0
  • i位置是右括号,如果dp[i-1]的前一位是左括号,则dp[i] 至少是 dp[i-1] + 2,此时需要再往前看一位,有可能扩大

2、实现

public static int maxLength(String s) {
    if (s == null || s.length() < 2) {
        return 0;
    }
    char[] str = s.toCharArray();
    int[] dp = new int[str.length];
    int pre = 0;
    int ans = 0;
    // dp[0] = 0;
    for (int i = 1; i < str.length; i++) {
        if (str[i] == ')') {
            pre = i - dp[i - 1] - 1; // 与str[i]配对的左括号的位置 pre
            if (pre >= 0 && str[pre] == '(') {
                dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
            }
        }
        ans = Math.max(ans, dp[i]);
    }
    return ans;
}