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

 

  

相關文章
相關標籤/搜索