LeetCode - 678. Valid Parenthesis String (DP | 思维)

LeetCode - 678. Valid Parenthesis String (DP | 思维)

  • DP
  • 思维

题目链接
题目

在这里插入图片描述

解析

这个题目有很多种解法。是一道练思维的好题。

DP

记忆化的思路:

  • 递归函数recur[L, R]范围内的字符串是否可以构成解,答案是recur(0, n-1)
  • 递归终止条件是L == R,如果此时chs[L] == '*',则按照题目要求是返回true的,或者L > R也返回true
  • 否则,我们检查L, R两个位置是否满足chs[L]在集合['(', '*']chs[R]在集合[')', '*']中,则如果[L+1, R-1](递归)如果能匹配,就返回true
  • 第二种情况,在[L, R]之间找到一个位置k,如果划分之后,[L, k][k+1, R]都能匹配,就返回true
  • 上述条件都不满足就返回false

在这里插入图片描述

记忆化代码:

class Solution {

    static int[][] dp;

    private boolean recur(char[] chs, int L, int R){
        if(L > R) // empty
            return true;
        if(L == R)
            return chs[L] == '*' ? true : false;
        if(dp[L][R] != 0)
            return dp[L][R] == 1 ? true : false;
        boolean res = false;
        if(check(chs, L, R))
            if(recur(chs, L+1, R-1))
                res = true;
        if(!res){ 
            for(int i = L; i <= R-1; i++){ // i <= R-1 not i <= R 
                if(recur(chs, L, i) && recur(chs, i+1, R)){ 
                    res = true;
                    break;
                }
            }
        }
        dp[L][R] = res == true ? 1 : -1;
        return res;
    }

    private boolean check(char[] chs, int L, int R){
        return (chs[L] == '(' && chs[R] == '*')
            || (chs[L] == '(' && chs[R] == ')')
            || (chs[L] == '*' && chs[R] == ')');
    }

    public boolean checkValidString(String s) {
        dp = new int[s.length()][s.length()];
        return recur(s.toCharArray(), 0, s.length()-1);
    }    
}

递推代码:

class Solution {

    public boolean checkValidString(String s) {
        if(s == null || s.length() == 0)
            return true;
        boolean[][] dp = new boolean[s.length()][s.length()];
        int n = s.length();
        char[] chs = s.toCharArray();
        for (int i = 0; i < n; i++){ 
            for (int j = 0; j < n; j++){
                if(i == j)
                    dp[i][j] = chs[i] == '*' ? true : false;
                else if(i > j)
                    dp[i][j] = true;
                else 
                    dp[i][j] = false;
            }
        }
        for (int i = n - 2; i >= 0; i--) {
            for (int j = i + 1; j < n; j++) {
                if (check(chs, i, j) && dp[i+1][j-1])
                    dp[i][j] = true;
                else {
                    for (int k = i; k < j; k++)
                        if (dp[i][k] && dp[k+1][j]) {
                            dp[i][j] = true;
                            break;
                        }
                }
            }
        }
        return dp[0][n - 1];
    }

    private boolean check(char[] chs, int L, int R) {
        return (chs[L] == '(' && chs[R] == '*')
            || (chs[L] == '(' && chs[R] == ')')
            || (chs[L] == '*' && chs[R] == ')');
    }
}
思维

和计数有关。

一开始也是按照下面的思路:

  • 用一个ln变量记录(的数量,star记录*的数量;
  • 如果遇到),就看前面的(数量,相当于每次遇到),先抵消掉前面的(,如果前面没有(,就抵消star
  • 如果遇到(,就累加ln,遇到*,就累加star

但是仅仅是上面的思路还不能得到正确结果,还需要反过来(从s.length() - 1 ~ 0) ,遇到(就像上面一样看之前的)的数量(抵消)。

这样两次之后没有问题,就返回true

class Solution {

    public boolean checkValidString(String s) {
        if(s == null || s.length() == 0)
            return true;
        int ln = 0, star = 0;
        for(int i = 0; i < s.length(); i++){ 
            char c = s.charAt(i);
            if(')' == c){ 
                if(ln > 0)
                    ln--;
                else if(star > 0)
                    star--;
                else 
                    return false;
            }else if('(' == c) 
                ln++;
            else 
                star++;
        }
        star = 0;
        int rn = 0;
        for(int i = s.length() - 1; i >= 0; i--){ 
            char c = s.charAt(i);
            if('(' == c){ 
                if(rn > 0)
                    rn--;
                else if(star > 0)
                    star--;
                else 
                    return false;
            }else if(')' == c)
                rn++;
            else 
                star++;
        }
        return true;
    }
}

其他思路:

在统计*的时候,维护(多出来的需要用*来匹配的star的数量。

class Solution {

    public boolean checkValidString(String s) {
        if(s == null || s.length() == 0)
            return true;
        int ln = 0, rn = 0, star = 0;
        int rightStar = 0;
        for(int i = 0; i < s.length(); i++){ 
            char c = s.charAt(i);
            if('(' == c){ 
                ln++;
            }else if(')' == c){ 
                rn++;
            }else {
                rightStar++;
                star++; // all star
            }
            if(rn > ln + star)
                return false;
            if(ln - rn < rightStar)
                rightStar = ln - rn;
            //rightStar = Math.min(rightStar, ln - rn);
        }
        if(ln - rn > rightStar)
            return false;
        return true;
    }
}

更多思路

class Solution {

    public boolean checkValidString(String s) {
        if(s == null || s.length() == 0)
            return true;
        int maxop = 0, minop = 0;
        for(int i = 0; i < s.length(); i++){ 
            char c = s.charAt(i);
            if(c == '(')
                minop++;
            else 
                minop--;
            if(c != ')')  // c == '(' || c == '*'
                maxop++;
            else 
                maxop--;
            if(maxop < 0)
                return false;
            if(minop < 0)
                minop = 0;
        }
        return minop == 0;         
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值