• 32. Longest Valid Parentheses


    ▶ 在一串 '(' 和 ')' 组成的字符串中寻找最长的匹配的子串,如 ()),(()),()(),()(()) 等都算合法。

    ● 代码,暴力搜索,超时。使用两个变量枚举原字符串的 O(n2) 个子串,每个子串用 O(n) 的时间去验证是否匹配,时间复杂度 O(n3),以下为稍微改进的版本,没有本质变化。

     1 class Solution
     2 {
     3 public:
     4     bool check(string & ss, int start, int end)
     5     {
     6         int i, count;
     7         for (i = start, count = 0; i <= end; i++)
     8         {
     9             ss[i] == '(' ? count++ : count--;
    10             if (count < 0)
    11                 return false;
    12         }
    13         return (count == 0);
    14     }
    15     int longestValidParentheses(string s)
    16     {
    17         if (s.size() <= 1)
    18             return 0;
    19 
    20         int i, j, maxLength;
    21         for (i = 1, maxLength = 0; i < s.size(); i++)
    22         {
    23             if (s[i] == '(')
    24                 continue;
    25             for (j = 0; j < i - maxLength; j++)// 每找到一个 ') '就从 s 开头开始寻找最长的合法串,
    26             {
    27                 if (check(s, j, i) && i - j + 1 > maxLength)// 找到的合法串比之前的更长,则更新 maxLength
    28                     maxLength = i - j + 1;
    29             }
    30         }
    31         return maxLength;
    32     }
    33 };

    ● 代码,愚蠢的动态规划,326 ms 。想到了用动态规划但是没有用对方法,使用数组 maxLength 来保存原字符串各前缀的最大匹配长度,还需要维护另外两个数组 maxStart 和 maxAtEnd(两者等价,不同情况下可使表达式稍微简化)来记录相关信息,且三个数组间相互关联不明显。

     1 class Solution
     2 {
     3 public:
     4     bool check(string & ss, int start, int end)// 检查 s 中下标从 start 到 end(两边都取得到)是否匹配
     5     {
     6         if (end < start || !((end - start) % 2))// 起点在终点右边,或者包含奇数个字符,肯定不匹配
     7             return false;        
     8         int i, count; 
     9         for (i = start, count = 0; i <= end; i++)
    10         {
    11             ss[i] == '(' ? count++ : count--;
    12             if (count < 0)
    13                 return false;
    14         }
    15         return (count == 0); 
    16     }
    17     int longestValidParentheses(string s)
    18     {
    19         if (s.size() <= 1)
    20             return 0;
    21         int i, j, temp;
    22         vector<int> maxLength(s.size(), 0);     // maxLength[i] 记录 s[0] ~ s[i] 最大匹配长度 
    23         vector<int> maxStart(s.size(), 0);      // maxStart[i] 记录 s[0] ~ s[i] 最大匹配长度的起点 
    24         vector<bool> maxAtEnd(s.size(), false); // maxAtEnd[i] = (maxStart[i] + maxLength[i] -1 == i),即最大匹配长度是否位于末尾
    25         
    26         for (i = 1; i < s.size(); i++)// 填表,每次循环在末尾添加一个字符,研究匹配情况
    27         {
    28             maxLength[i] = maxLength[i - 1];
    29             maxStart[i] = maxStart[i - 1];
    30             if (s[i] == '(')
    31                 continue;
    32             if (maxAtEnd[i - 1])// 长为 i 的前缀(下标为 i - 1)中最长匹配位于末尾
    33             {
    34                 if (maxStart[i - 1] > 0 && s[maxStart[i - 1] - 1] == '(')// 检查该匹配前一个字符是否为 '(',否则不能成为更长的匹配
    35                 {
    36                     for (j = maxStart[i - 1] - 2 - maxLength[maxStart[i - 1] - 2];
    37                         j < maxStart[i - 1] - 2 && !check(s, j, maxStart[i - 1] - 2); j++);
    38                         // 以上面找到的 '(' 左侧的字符为起点, 继续往前寻找匹配。
    39                         // 不一定是该范围内的最长匹配,但长度不超过该范围的最大匹配长度 maxLength[maxStart[i - 1] - 2]
    40                     if (j < maxStart[i - 1] - 2)// 找到了新的最长匹配,更新数据
    41                     {
    42                         maxLength[i] = i - j + 1;
    43                         maxStart[i] = j;                        
    44                     }
    45                     else// 没有找到新的最长匹配,仅在原来的基础上扩展一对括号
    46                     {
    47                         maxLength[i] += 2;
    48                         maxStart[i] -= 1;
    49                     }
    50                     maxAtEnd[i] = true;
    51                 }
    52             }
    53             else if (check(s, temp = maxStart[i - 1] + maxLength[i - 1], i))// 恰能在原匹配的后面接上一个匹配
    54             {
    55                 maxLength[i] += i - temp + 1;
    56                 maxAtEnd[i] = true;
    57             }
    58             else    // 完全不能与原匹配产生联系,在后半段逐个查找
    59             {
    60                 for (j = temp + 1; j < i && i - j + 1 >= maxLength[i]; j++)
    61                 {
    62                     if (check(s, j, i))
    63                     {
    64                         maxLength[i] = i - j + 1;
    65                         maxStart[i] = j;
    66                         maxAtEnd[i] = true;
    67                     }
    68                 }
    69             }
    70         }
    71         return maxLength[s.size() - 1];
    72     }
    73 };

    ● 代码,正确的动态规划,10 ms 。数组 dp[ i ] 记录的是 “以 s[ i ] 为结尾的子串的最大匹配长度”,可以根据 s[ i - 1 ] 的值简单的分为两种情况。

     1 class Solution
     2 {
     3 public:
     4     int longestValidParentheses(string s)
     5     {
     6         int output = 0;
     7         vector<int> dp(s.size());
     8         for (int i = 1; i < s.length(); i++)
     9         {
    10             if (s[i] == ')')
    11             {
    12                 if (s[i - 1] == '(')                        // "...(...√...)()" 型,使用 dp[i-2] 来计算 
    13                     (i >= 2) ? (dp[i] = dp[i - 2] + 2) : (dp[i] = 2);
    14                 else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(')//  "(...√...)((...√...))" 型,要计算 dp[i-1] 以及更前面的那个
    15                     (i - dp[i - 1] >= 2) ? (dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2) : (dp[i] = dp[i - 1] + 2); 
    16                 output = (dp[i] > output) ? dp[i] : output;
    17             }
    18         }
    19         return output;
    20     }
    21 };

    ● 代码,栈法,13 ms 。

     1 class Solution
     2 {
     3 public:
     4     int longestValidParentheses(string s)
     5     {
     6         int output = 0;
     7         stack<int> st;
     8         st.push(-1);
     9         for (int i = 0; i < s.length(); i++)
    10         {
    11             if (s[i] == '(')
    12                 st.push(i);
    13             else // 遇 ')' 出栈并计算
    14             {
    15                 st.pop();
    16                 if (st.empty())
    17                     st.push(i);
    18                 else
    19                     output = (i - st.top() > output) ? i - st.top() : output;
    20             }
    21         }
    22         return output;
    23     }
    24 };

    ● 代码,神奇的左右扫描法,13 ms 。两个方向扫描数组,计算最大匹配长度作为输出。

     1 class Solution
     2 {
     3 public:
     4     int longestValidParentheses(string s)
     5     {
     6         int left = 0, right = 0, maxLength = 0;
     7         for (int i = 0; i < s.length(); i++)        // 从左往右扫描,相等时计算最大匹配长度,')' 不少于 '(' 时计数归零
     8         {
     9             (s[i] == '(') ? left++ : right++;
    10             if (left == right)
    11                 maxLength = (2 * right > maxLength) ? 2 * right : maxLength; 
    12             else if (right >= left)
    13                 left = right = 0;
    14         }
    15         left = right = 0;
    16         for (int i = s.length() - 1; i >= 0; i--)   // 从右往左扫描,相等时计算最大匹配长度,'(' 不少于 ')' 时计数归零
    17         {
    18             (s[i] == '(') ? left++ : right++;
    19             if (left == right)
    20                 maxLength = (2 * left > maxLength) ? 2 * left : maxLength;
    21             else if (left >= right)
    22                 left = right = 0;
    23         }
    24         return maxLength;
    25     }
    26 };
  • 相关阅读:
    IOS 修改UISearchBar 输入框的颜色 placeholder字体的颜色
    iOS-集成微信支付和支付宝支付
    iOS-集成极光推送
    常用iOS开发网站资源
    iOS -媒体播放器 AVPlayer 与 AVPlayerViewController
    使用终端统计代码行数
    swift 与 OC中的需要注意知识点
    去掉tableview顶部留白
    解决UIScrollView把uitableviewcell的点击事件屏蔽
    按钮点击发光效果
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/8284095.html
Copyright © 2020-2023  润新知