Longest Valid Parentheses
Stack of indices anchored by the last invalid position; on each match, length = i - stack top.
3 min read
stack dp
Problem#
Given a string of ( and ), return the length of the longest valid (well-formed) parentheses substring.
Examples#
"(()"→2")()())"→4""→0
Constraints#
0 <= s.length <= 3 * 10^4.
Approach#
Push indices of ( and unmatched ). Initialize the stack with -1 as a sentinel “last invalid index”. For (, push the index. For ), pop. If the stack is empty, push the current index as the new invalid anchor. Otherwise, the current valid length is i - stack.top().
Solution#
class Solution {public: int longestValidParentheses(string s) { stack<int> st; st.push(-1); int ans = 0; for (int i = 0; i < (int)s.size(); ++i) { if (s[i] == '(') st.push(i); else { st.pop(); if (st.empty()) st.push(i); else ans = max(ans, i - st.top()); } } return ans; }};func longestValidParentheses(s string) int { st := []int{-1} ans := 0 for i := 0; i < len(s); i++ { if s[i] == '(' { st = append(st, i) } else { st = st[:len(st)-1] if len(st) == 0 { st = append(st, i) } else { if cur := i - st[len(st)-1]; cur > ans { ans = cur } } } } return ans}class Solution: def longestValidParentheses(self, s: str) -> int: st = [-1] ans = 0 for i, ch in enumerate(s): if ch == '(': st.append(i) else: st.pop() if not st: st.append(i) else: ans = max(ans, i - st[-1]) return ansfunction longestValidParentheses(s) { const st = [-1]; let ans = 0; for (let i = 0; i < s.length; i++) { if (s[i] === '(') { st.push(i); } else { st.pop(); if (st.length === 0) { st.push(i); } else { const cur = i - st[st.length - 1]; if (cur > ans) ans = cur; } } } return ans;}class Solution { public int longestValidParentheses(String s) { Deque<Integer> st = new ArrayDeque<>(); st.push(-1); int ans = 0; for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '(') { st.push(i); } else { st.pop(); if (st.isEmpty()) { st.push(i); } else { ans = Math.max(ans, i - st.peek()); } } } return ans; }}function longestValidParentheses(s: string): number { const st: number[] = [-1]; let ans = 0; for (let i = 0; i < s.length; i++) { if (s[i] === '(') { st.push(i); } else { st.pop(); if (st.length === 0) { st.push(i); } else { const cur = i - st[st.length - 1]; if (cur > ans) ans = cur; } } } return ans;}Editorial#
The stack top always holds the index just before the current valid stretch starts. When we close a pair successfully, the length is i - newTop. Pushing -1 initially makes the math work for valid prefixes; pushing the unmatched ) index when the stack empties resets the anchor.
Complexity#
- Time: O(n).
- Space: O(n).
Concept revision#
Related#