題目描述:給定一個只含 '(' 和 ')' 的字串 s,每次操作可在任意位置插入一個括號,求使 s 成為合法括號字串(每個左括號有對應右括號,且配對正確)所需的最少插入次數。
解題思路:
最直觀的方式是用堆疊模擬括號配對,但本題可以用更簡潔的貪心計數方法,只需兩個變數即可。
兩個計數器的含義:
open:目前尚未配對的左括號數量(需要在右側補右括號)。close:目前已無法配對、需要補左括號的右括號數量(缺少左括號)。遍歷規則:
'(':open++(多了一個等待配對的左括號)。')':
open > 0:可以用前面的左括號配對,open--。open == 0:沒有可配對的左括號,這個右括號是「孤立」的,需要在其前面補一個左括號,close++。最終答案:open + close。
open 個未配對的左括號,每個需要在後面補一個右括號。close 個孤立的右括號,每個已計入需要補的左括號數。範例說明:
s = "())("
'(' → open = 1
')' → open > 0,open = 0(配對)
')' → open == 0,close = 1(孤立右括號)
'(' → open = 1
結果:open + close = 1 + 1 = 2
需補:在最後補 ')' 以配對第四個 '(',在第三個 ')' 前補 '('
時間複雜度:O(n) — 只需線性掃描字串一次,每個字元進行常數時間的判斷與計數更新。
空間複雜度:O(1) — 只使用兩個整數計數器,不需要額外的堆疊或陣列。
函式 minAddToMakeValid(s):
1. 設 open = 0,close = 0
2. 對每個字元 c 在 s 中:
a. 若 c == '(':open 加 1
b. 若 c == ')':
- 若 open > 0:open 減 1(配對成功)
- 否則:close 加 1(孤立右括號,需補左括號)
3. 回傳 open + close用堆疊逐字元處理,遇到 '(' 入棧,遇到 ')' 時若棧非空則彈出(配對),否則計入缺少左括號的數量。最終棧中剩餘的 '(' 數量即為需補的右括號數。
stack<char> st;
int close = 0;
for (char c : s) {
if (c == '(') st.push(c);
else if (!st.empty()) st.pop();
else close++;
}
return st.size() + close;
時間複雜度 O(n),空間複雜度 O(n)。邏輯與計數法等價,但使用了額外空間。
維護一個「平衡值」bal,遇到 '(' 則 +1,遇到 ')' 則 -1。若 bal < 0,說明出現孤立右括號,需補一個左括號:將 bal 重置為 0,並計入一次插入。最後 bal 的值即為需補的右括號數。
此方法本質上與計數法相同,只是用單一變數表達,但需要額外一個累計計數器,程式碼上與計數法等價。
將字串分為左右兩半,分別計算需要補的括號數,再合併。這種方法時間複雜度 O(n log n),沒有實際優勢,僅作為思考練習。
Leetcode 1249 — Minimum Remove to Make Valid Parentheses(最少移除使括號合法) 是本題的對偶問題:本題是最少插入,彼題是最少刪除。兩題的計數邏輯相同,但操作方向相反,請比較兩題解法的異同。
若字串中除了括號還包含其他字元(字母、數字等),演算法需要如何調整? 答案是幾乎不需要調整——只需跳過非括號字元繼續處理即可。請思考為什麼這種修改是正確的。
Leetcode 32 — Longest Valid Parentheses(最長合法括號子串) 是更難的延伸題,求最長的合法括號子串長度。本題的計數邏輯可以作為子程序使用,但核心難點在於如何定位合法子串的邊界,請思考如何利用堆疊或 DP 解決。