爨爨爨好

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

● 代码,暴力搜索,超时。使用两个变量枚举原字符串的 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 };

 

posted on 2018-01-14 19:55  爨爨爨好  阅读(143)  评论(0编辑  收藏  举报