最長合法括號子序列
一個合法的括號序列滿足以下條件:
- 序列()被認為是合法的。
- 如果序列X與Y是合法的,則XY也被認為是合法的。
- 如果序列X是合法的,則(X)也是合法的。
例如, () , ()() , (()) 這些都是合法的。
現在,給定一個由 ( 和 ) 組成的字符串。
請你求出其中的最長合法括號子序列的長度。
注意,子序列不一定連續。
輸入格式
共一行,一個由 ( 和 ) 組成的字符串。
輸出格式
一個整數,表示最長合法括號子序列的長度。
數據范圍
前五個測試點滿足,$1 \leq \text{輸入字符串的長度} \leq 10$。
所有測試點滿足,$1 \leq \text{輸入字符串的長度} \leq {10}^{6}$。
輸入樣例1:
(()))(
輸出樣例1:
4
輸入樣例2:
()()(()(((
輸出樣例2:
6
解題思路
這是一個括號序列的問題。對於一個合法的括號序列,有兩個結論:
- 整個序列的左右括號數量相等。
- 任意一個前綴中,左括號的數量大於等於右括號的數量。
對於這兩個條件,一般用一個計數器來實現。一開始$cnt = 0$,遇到左括號就$cnt++$,遇到右括號就$cnt--$。等價於在上面的第$1$個條件中,最后的$cnt = 0$;第$2$個條件中,整一個操作的過程$cnt$都始終滿足$cnt \geq 0$。
一個合法括號序列最長,等價於右括號數量最多(因為左右括號數量相同),因此我們只需要找到一個合法括號序列,使得右括號的數量最多就可以了。
貪心的思想是,對於右括號,能選則選。如果在遍歷的過程中遇到右括號,且$cnt > 0$,就選這個右括號。下面證明一下這種做法是正確的。
首先有貪心解$\leq$最優解,因為貪心解是合法解,最優解是合法解中最大的那一個,因此貪心解$\leq$最優解。
下面證明貪心解$\geq$最優解。反證法,假設有最優解$>$貪心解。
因此可以證明得到貪心解$\leq$最優解,貪心解$\geq$最優解,即可以證明貪心解$=$最優解。
AC代碼如下:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int N = 1e6 + 10; 6 7 char str[N]; 8 9 int main() { 10 scanf("%s", str); 11 12 int l = 0, r = 0; 13 for (int i = 0; str[i]; i++) { 14 if (str[i] == '(') l++; 15 else if (l > 0) l--, r++; 16 } 17 18 printf("%d", r << 1); 19 20 return 0; 21 }
參考資料
AcWing 4207. 最長合法括號子序列(AcWing杯 - 周賽):https://www.acwing.com/video/3660/