首页 > 代码库 > Longest Valid Parentheses

Longest Valid Parentheses

Given a string containing just the characters ‘(‘ and ‘)‘, find the length of the longest valid (well-formed) parentheses substring.

For "(()", the longest valid parentheses substring is "()", which has length = 2.

Another example is ")()())", where the longest valid parentheses substring is "()()", which has length = 4.

也就是说要找到最长的子串满足匹配规则。

算法思路,建立两个数组pre[]和post[],生成规则如下:

pre数组:从前向后遍历原字符串s,若该下标i所对应的字符为‘(‘,则 :(1)若i为0,pre[i]=1;(2)若pre[i-1]<0,pre[i]=1(3)其余情况,pre[i]=pre[i-1]+1;

post数组:从后向前遍历原字符串s,若该下标i所对应的字符为‘)‘,则:(1)若i为s.length-1,post[i]=1;(2)若post[i+1]<0,pre[i]=1(3)其余情况,pre[i]=pre[i+1]+1;

例如:字符串")()())"的pre数组为 -1  1  0  1  0  -1,post数组为2  1  2  1  2  1

然后在pre数组中从后向前找到第一个值为0的下标,若没有,则从pre数组中找出的最长子串为0,若找到,再向前找出第一个小于0的下标,或者全部大于0,此时下标递减到0,则从pre数组中找出的最长子串的长度就是这两个下标的差值。

同理,在post数组中从前向后找第一个值为0的下标,然后再找第一个值小于0的下标或者下标到s.length-1,从post数组中找到的子串长度即这两个下标的差值。

因此,满足条件的最长子串的长度就是从pre数组和post数组中找到的长度的最大值。

int pre[] = new int[s.length()];
        int post[] = new int[s.length()];
        int start = 0;
        int end = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ‘(‘) {
                if (i == 0) {
                    pre[i] = 1;
                } else {
                    if (pre[i - 1] < 0) {
                        pre[i] = 1;
                    } else {
                        pre[i] = pre[i - 1] + 1;
                    }
                }
            } else {
                if (i == 0) {
                    pre[i] = -1;
                } else {
                    pre[i] = pre[i - 1] - 1;
                }
            }
        }
        int pos = 0;
        pos = s.length();
        for (int i = s.length() - 1; i >= 0;) {
            while (pre[i] != 0) {
                i--;
                if (i < 0) {
                    break;
                }
            }
            pos=i;
            while (i>=0&&pre[i]>=0){
                i--;
            }
            start = (start>(pos-i))?start:(pos-i);
        }
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) == ‘)‘) {
                if (i == s.length() - 1) {
                    post[i] = 1;
                } else {
                    if (post[i + 1] < 0) {
                        post[i] = 1;
                    } else {
                        post[i] = post[i + 1] + 1;
                    }
                }
            } else {
                if (i == s.length() - 1) {
                    post[i] = -1;
                } else {
                    post[i] = post[i + 1] - 1;
                }
            }
        }
        
        pos = 0;
        for (int i = 0; i < s.length();) {
            while (post[i] != 0) {
                i++;
                if (i >= s.length()) {
                    break;
                }
            }
            pos=i;
            while (i<s.length()&&post[i]>=0){
                i++;
            }
            end = (end>(i-pos))?end:(i-pos);
        }
        return (start < end) ? end : start;

因为算法中遍历了两次原字符串,一次pre数组和一次post数组,因此时间复杂度为O(n),空间复杂度为两个数组pre和post,为O(n)

////////////////////////////////////////////////////////////////////////////////////////////分割线/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

看了一下网上的做法,转载一下:

转自http://blog.csdn.net/cfc1243570631/article/details/9304525

这道题可以用一维动态规划逆向求解。假设输入括号表达式为String s,维护一个长度为s.length的一维数组dp[],数组元素初始化为0。 dp[i]表示从s[i]到s[s.length - 1]包含s[i]的最长的有效匹配括号子串长度。则存在如下关系:

  • dp[s.length - 1] = 0;
  • i从n - 2 -> 0逆向求dp[],并记录其最大值。若s[i] == ‘(‘,则在s中从i开始到s.length - 1计算dp[i]的值。这个计算分为两步,通过dp[i + 1]进行的(注意dp[i + 1]已经在上一步求解):
    • 在s中寻找从i + 1开始的有效括号匹配子串长度,即dp[i + 1],跳过这段有效的括号子串,查看下一个字符,其下标为j = i + 1 + dp[i + 1]。若j没有越界,并且s[j] == ‘)’,则s[i ... j]为有效括号匹配,dp[i] =dp[i + 1] + 2。
    • 在求得了s[i ... j]的有效匹配长度之后,若j + 1没有越界,则dp[i]的值还要加上从j + 1开始的最长有效匹配,即dp[j + 1]。