leetcode: Longest Valid Parentheses分析和实现

  题目大意:给出一个只包含字符'('和')'的字符串S,求最长有效括号序列的长度。数组

  


  颇有趣的题目,有助于咱们对这种人类自身制定的规则的深刻理解,可能咱们大多数人都从没有真正理解过怎样一个括号序列是有效的,所以解题也无从提及。整道题目的难度在于咱们对有效括号序列的理解和定义。下面给出我本身的定义:、ide

  定义1:空括号序列是有效的。spa

  定义2:对于一对左右括号,若左括号出如今右括号的左边,且左右括号之间(不包含两端)的括号序列是有效的,那么称该左括号到该右括号(包含)这一段序列是有效的。且称该左括号和右括号匹配。code

  定义3:若一个括号序列中每个左括号都有与之匹配的右括号,而每个右括号也有与之匹配的左括号,则称该括号序列是有效的。定义3是对定义2的一个扩展。blog

  对于定义的正确性与我的思惟方式有关故不作说明,我一直认为没法证实是错误的东西,就是正确的,至少你能直接拿过来使用。下面给出一些记号的定义:S[x~y]表示由S[x],S[x+1],...,S[y-1],S[y]按前后顺序组成的子序列;n表示S的长度。后面再也不对这些记号作说明。leetcode

  


  命题1:对于任意一个有效括号序列P,其中每个左括号惟一匹配一个右括号,同时右括号也惟一匹配左括号。字符串

  证实:定义3已经给出了有效序列中每一个左括号与某个右括号匹配,只是缺乏惟一性的证实,下面进行说明。假设对于某个下标为A的左括号同时下标为B与下标为C的两个右括号相匹配。不妨假设B<C。则因为A和B相匹配,由定义2能够得出序列P[A+1~C-1]是有效序列,而A+1<=B<=C-1,故能为找到下标为D的左括号,使得A+1<=D<B成立,且D与B想匹配(定义3)。能够重复上面的过程找到下标为E的右括号使得D<E<B,且D与E相匹配,从而无限推导下去,可是咱们所考察的有效区间从A,C转为A,B,以后转为E,B,这是一个不断递减的过程,所以这个重复动做总会在某个点失败,而这时候就能反证假设错误,故惟一性得证。数学

 

  命题1':若P和P[x~y]均为有效序列,那么在P中下标为x的左括号必然与下标为y的右括号惟一匹配。it

  证实:首先因为P[x~y]是有效序列,所以显然在P中下标为x的左括号与下标为y的右括号是匹配的。而由命题1便可直接得出匹配的惟一性。io

 

  命题2:对于一段括号序列S[x~y]是有效的等价于:若对于任意知足x <= i <= y的整数i,有S[x~i]中左括号数很多于右括号数以及S[i~y]中右括号数很多于左括号数两个性质同时成立。

  证实:

  对于必要性,因为S[x~y]中每个左括号都有对应的右括号与之惟一相匹配,所以对于任意知足x <= i <= y的整数i,有S[x~i]中左括号数很多于右括号数(不然S[x~i]中多余的右括号则在S[x~y]没有与之匹配的左括号)。而S[i~y]中右括号数很多于左括号数性质也能够相似推出。

  而对于充分性,利用数学概括法。当y = x + 1时,命题显然成立,由于知足这一条件的子序列只多是"()"。现假设y <= x + t时(t >= 1),命题成立。对于任意在S[x~y]之间出现的左括号,记其下标为L。因为S[L~y]中右括号很多于左括号数,所以必能找到最小的下标R,使得L<R且S[L~R]中出现的左括号数和右括号数数量一致。下面验证L和R是匹配的。首先L<R是不言而喻的,只要证实S[L+1~R-1]是有效的。因为下标R最小,所以能够获得对于任意知足L<m<R的整数m,都有S[L~m]中左括号数多于右括号数,不然与R的定义相悖。而一样能保证L是最大的知足L<R且S[L~R]中左括号与右括号数目一致的下标(利用反证法,设能够找到L<K<R使得S[K~R]中左括号与右括号数目一致,那么必然有S[L~K-1]中左括号与右括号数目一致),故对应的能够得出S[m~R]中右括号多于左括号数。利用这两个性质能够等价得出:对于任意知足L+1<=m<=R-1的整数m,都有S[L+1~m]中左括号数多于右括号数且S[m~R-1]中右括号多于左括号数。而(R-1)-(L+1)=R-L-2且y<=x+t+2能够推出R<=L+t,所以利用前面数学概括法获得的结论(假设y <= x + t时命题成立)能够推出S[L+1~R-1]有效,即S[L~R]是有效的,再借助定义3能够得出序列S[x~y]是有效的。命题得证。

  

  命题3:若S[A~B]是有效序列,且S[B+1~C]是有效序列,则S[A~C]也是有效序列。

  证实:S[A~B]有效且S[B+1~C]是有效序列,则S[A~C]必然知足命题2右边的性质,故S[A~C]是有效序列。

 

  命题3':若S[A~C]是有效序列,且对于A<B<C,S[A~B]是有效序列,则S[B+1~C]是有效序列。

  证实:依旧是利用命题2右边的性质能够直接得出S[B+1~C]是有效序列。


  原本考虑的时候是但愿能利用命题2给出的结论进行快速计算,后来发现动态规划彷佛更加简单,也更容易证实。建立长度为n的数组DP,令DP[i]记录如下标i做为左边界的最大有效括号序列的长度,能够发现下面的递推关系:

  1.若S[i]是右括号,则DP[i]=0

  2.若S[i]是左括号,且S[i+1]是右括号,则DP[i]=2+DP[i+2]

  3.若S[i]是左括号,且S[i+1]是左括号,且S[DP[i+1]+i+1]是右括号,则DP[i]=DP[i+1]+2+DP[DP[i+1]+i+2]

  4.若S[i]是左括号,且S[i+1]是左括号,且S[DP[i+1]+i+1]是左括号,则DP[i]=0

  下面进行正确性的说明。

  1的正确性不言而喻。

  对于2,因为序列S[i~i+1+DP[i+2]]知足命题2右边的条件,所以序列有效。假设存在j > i + 1 + DP[i+2],使得S[i~j]有效,那么命题3'将导出S[i+2~j]是有效序列这样一个结论,这与DP的定义相悖。

  对于3,一样序列知足S[i~i+1+DP[i+1]]命题2右边的条件,所以序列有效。根据命题1',此时S[i]与S[DP[i+1]+i+1]惟一匹配。若咱们忽略S[i+1~i+DP[i+1]]部份内容,则能够归结为状况2,利用相同的思路证实便可。

  对于4,能与左括号S[i]匹配的右括号的下标j只可能位于i+1与DP[i+1]+i+1(不然命题3'将导出与DP的定义相悖的结论)。而因为j不多是DP[i+1]+i+1,所以j取i+1与DP[i+1]+i之间。可是因为i+1与DP[i+1]+i之间的任意下标为k的右括号,都必然会导出S[i~k]中左括号数多于右括号数目的现象,所以不存在与S[i]相匹配的右括号。

  利用上面的递推公式和动态规划的技术,能够在O(n)的时间复杂度和空间复杂度内获得最终结果,最长的有效序列的长度。


 

  下面给出实际代码:

 1 package cn.dalt.leetcode;
 2 
 3 /**
 4  * Created by dalt on 2017/8/26.
 5  */
 6 public class LongestValidParentheses {
 7     private static final char LEFT = '(';
 8     private static final char RIGHT = ')';
 9 
10     public int longestValidParentheses(String s) {
11         int n = s.length();
12         char[] data = s.toCharArray();
13         int[] dp = new int[n + 1];
14         int max = 0;
15         for (int i = n - 1; i >= 0; i--) {
16             int nextIndex = i + 1;
17             //condition 1
18             if (data[i] == RIGHT) {
19                 dp[i] = 0;
20             }
21             //check for not exceeding the bound
22             else if (nextIndex >= n) {
23                 dp[i] = 0;
24             } 
25             //condition 2
26             else if (data[nextIndex] == RIGHT) {
27                 dp[i] = dp[i + 2] + 2;
28             } 
29             //condition 3 and 4
30             else if (data[nextIndex] == LEFT) {
31                 int endIndex = dp[nextIndex] + nextIndex;
32                 //check for not exceeding the bound
33                 if (endIndex >= n || data[endIndex] == LEFT) {
34                     dp[i] = 0;
35                 } 
36                 //conditon 4
37                 else {
38                     //DP[i]=DP[i+1]+2+DP[DP[i+1]+i+2]
39                     dp[i] = dp[nextIndex] + 2 + dp[endIndex + 1];
40                 }
41             }
42 
43             if (dp[i] > max) {
44                 max = dp[i];
45             }
46         }
47         return max;
48     }
49 }
View Code

 

  

相关文章
相关标签/搜索