Opening the reading…
Opening the reading…
STACK › PARENTHESES PROBLEM
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A valid substring cannot cross an unmatched closing parenthesis. Once a closing parenthesis has nothing to match, every substring ending at or before that position is separated from anything after it, so that index becomes a new boundary. Between boundaries, unmatched opening parentheses are the only information that still matters.
Store indices of unmatched opening parentheses, with one extra boundary index at the bottom of the stack. For a closing parenthesis, remove one possible opening match. If an opening remains below it, the valid substring starts just after that index, so its length is the current index minus the stack top. If no opening remains, the close is unmatched and its own index becomes the next boundary.
DIAGRAM — NOT DRAWN YET
A row of parentheses is labelled with zero-based indices, and a stack beside it contains the sentinel boundary and indices of unmatched opening parentheses. A pointer marks the current closing parenthesis. When an opening index remains below the popped entry, the span after the stack top through the current index is highlighted as valid. When the stack becomes empty, the current closing index is shown as the new boundary. The picture makes clear that the top index is the position just before the valid suffix.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | Each character is processed once, and each index is pushed and popped at most once. The stack operations are constant time, so the total work grows directly with the number of characters. |
| Space | O(n) | The answer is a scalar, while the stack is extra working memory and can contain all n opening-parenthesis indices in an input such as (((...(. The returned length is output rather than working memory and is excluded. |
#include <algorithm>
#include <stack>
#include <string>
using namespace std;
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> st;
st.push(-1);
int ans = 0;
for (int i = 0; i < static_cast<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;
}
};The sentinel -1 is not a parenthesis; it is a virtual position before the string. That makes i - st.top() equal to the correct length when a valid substring starts at index 0. The empty-stack branch is equally important: after an unmatched close, pushing i prevents later valid characters from being measured across that invalid position.
Dynamic programming can store the length of the longest valid substring that ends exactly at each index. A closing parenthesis can extend the valid suffix ending at i - 1, then possibly join the valid block before its matching opening parenthesis. This avoids an explicit stack, but dp stores one value per character, so it is not a space optimisation; it is a different arrangement of the same linear scan.
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
int longestValidParentheses(string s) {
int n = static_cast<int>(s.size());
vector<int> dp(n, 0);
int ans = 0;
for (int i = 1; i < n; ++i) {
if (s[i] != ')') {
continue;
}
int opening = i - dp[i - 1] - 1;
if (opening >= 0 && s[opening] == '(') {
dp[i] = dp[i - 1] + 2;
if (opening > 0) {
dp[i] += dp[opening - 1];
}
ans = max(ans, dp[i]);
}
}
return ans;
}
};