[Leetcode] 最长有效括号

关键词:DP,动态规划,动规。

最近在刷DP专栏的题目,这是其中一道题。

给定一个只包含 '('')' 的字符串,找出最长的包含有效括号的子串的长度。

longest-valid-parentheses

Sample1

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

Sample2

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

对于DP嘛,首先还是需要抽象出状态函数:

dp[i] 表示:以 S[i] 结尾的,最长有效括号串的长度。

然后是状态转移方程:

如果 s.len == 0 or s.len == 1,那么返回 0 .
否则:
	if s[i]=='(' then dp[i]=0
	if s[i]==')' then:
		if s[i-1]='(' then dp[i] = dp[i-2] + 2 (数组下标是否越界,即 i>=2? )
		if s[i-1]=')' then:
			if s[i-dp[i-1]-1] == '(' then dp[i] = dp[i-dp[i-1]-2] + dp[i-1] + 2 (是否越界?)
			if s[i-dp[i-1]-1] == ')' then dp[i] = 0

第一个 if 语句表示:形如 ....( 这样的字符串,是必然不合法的。

第二个 if 语句表示:形如 ....) 这样的字符串,这样我们需要考虑 s[i-1]:

  • s[i-1]='(': 字符串形如 ...(),显然,下标对应如下:

    i-2   i-1    i
     x     (     )       
    

    显然,dp[i] 的值应当是 dp[i-2] + 2

  • s[i-1]='): 字符串形如 ...)),显然,下标对应如下:

    ?          i-1    i
    x   (...    )     )
    

    现考虑与 s[i-1] 匹配的 左括号的位置s[i-1]=')' ,其合法的括号串长度是 dp[i-1] ,那么 '(' 的位置应当是:

    i - 1 - (dp[i-1] - 1) = i - dp[i-1]
    

    也就是说,s[i] = ) 匹配的左括号位置应当是:i - dp[i-1] - 1.

    i-dp[i-1]-1  i-dp[i-1]        i-1    i
        x            (      ...    )     )
    

    如果 x = s[i-dp[i-1]-1] == ( ,那么: dp[i] = dp[i-dp[i-1]-2] + dp[i-1] + 2 (因为 “ 没画出来的 ” 前面仍有可能是有效的括号串)

    但是如果 i-dp[i-1]-1 这个位置的符号不是 ( 呢?这就说明以 s[i] 为结尾的括号传不是合法的,即:d[i] = 0

完整代码:

需要特别注意数组下标越界的问题,一旦越界,说明前面不是一个有效的括号串。

class Solution {
public:
    int longestValidParentheses(string s) 
    {
        int n = s.length();
        if (n <= 1) return 0;
        vector<int> dp(n, 0);
        auto getdp = [&](int idx) { return 0 <= idx && idx < n ? dp[idx] : 0; };
        for (int i=1; i<n; i++)
        {
            if (s[i] == ')')
            {
                if (s[i-1] == '(') dp[i] = getdp(i-2) + 2;
                else if (s[i-1] == ')')
                {
                    int idx = i - getdp(i-1) - 1;
                    if (idx >= 0 && s[idx] == '(')
                        dp[i] = getdp(i-1) + getdp(idx-1) + 2;
                }
            }
        }
        return *max_element(dp.begin(), dp.end());
    }
};
/*
dp[i] 表示以 s[i] 结尾的最长有效括号长度。
if s[i] == '(': dp[i] = 0
if s[i] == ')': 
    if s[i-1] == '(': dp[i] = dp[i-2] + 2
    if s[i-1] == ')' && s[i-dp[i-1]-1] == '(': dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2

        +-------match-------+
        |                   |
      ) ( (       ...  )    )
      ^   ^            ^    ^
      |   i-dp[i-1]    i-1  i 
      i-dp[i-1]-2
*/
posted @ 2019-09-13 13:50  sinkinben  阅读(165)  评论(0编辑  收藏  举报