思路
贪心。从左往右统计以上一段非法序列后的下一左括号开头的最长合法序列长度,在下图的例子中,即只统计以 $s_1$ 开头的最长合法序列长度 $f(1)$。
123456789
((((()(((
若同时存在以 $s_1, s_2, s_3, s_4, s_5$ 开头的合法序列,则 $f(1) > f(2) > f(3) > f(4) > f(5)$,可反证。仅证 $f(1) > f(2)$:
性质:序列合法的必要条件是所有前缀的左括号数量大于等于右括号数量,且整个序列的左括号数量等于右括号数量。
12 i j
---------------
--------------------------------
如图所示,若 $f(1) = i, f(2) = j - 1$,且 $f(1) \le f(2)$,则由于 $[1, i]$ 合法,且 $s_1$ 是左括号,因此 $[2, i]$ 内左括号数量小于右括号数量,而 $[2, j]$ 也合法,且 $[2, i]$ 是 $[2, j]$ 的前缀,这与合法序列所有前缀的左括号数量大于等于右括号数量矛盾。
若不存在以 $s_1$ 开头的合法序列,则可能遗漏以 $s_2, s_3, s_4, s_5$ 开头的合法序列。
从右往左统计以上一段非法序列后(向左)的下一右括号开头的最长合法序列长度,在上图的例子中,即只统计以 $s_6$ 开头(向左)的最长合法序列长度。
可证明,若从左往右不存在以 $s_1$ 开头的合法序列,且存在以 $s_2, s_3, s_4, s_5$ 之一开头的合法序列(第一个最长),则该序列(最长的那个)一定会被从右往左统计到。
(((...(((...)))(....)
u i j k
如上图所示,其中第一段省略号表示若干左括号,第二段省略号表示一段合法序列,第三段省略号表示一段非法序列。假设不存在以 $s_u$ 开头的合法序列,但存在以 $s_i$ 开头的合法序列(它是 $s_u$ 后第一个出现的合法序列),且该合法序列最长到 $s_j$ 结尾。
首先,$s_j$ 后紧跟的一定是左括号,否则,$s_i$ 就不是 $s_u$ 后第一个出现的合法序列开头。且 $s_{j + 1}$ 一定不是一段合法序列开头,否则以 $s_i$ 开头的最长合法序列的结尾就不是 $s_j$。因此从右往左计算时,$s_j$ 是一段非法序列向左的第一个右括号,会从它开始向左计算统计到 $s_i$。
时间复杂度 $O(n)$。
#include <iostream>
#define endl "\n"
using namespace std;
void cal(auto it, auto end, char open, int &mx, int &cnt) {
mx = 0, cnt = 1;
for (int b = 0, l = 0; it != end; it++) {
if (*it == open)
b++;
else
b--;
if (b < 0)
b = 0, l = 0;
else
l++;
if (!b && l) {
if (l > mx)
mx = l, cnt = 1;
else if (l == mx)
cnt++;
}
}
}
int main() {
cin.tie(0)->sync_with_stdio(0);
string s;
cin >> s;
int mxl, cntl, mxr, cntr;
cal(s.begin() , s.end(), '(', mxl, cntl);
cal(s.rbegin(), s.rend(), ')', mxr, cntr);
if (mxl < mxr)
mxl = mxr, cntl = cntr;
cout << mxl << " " << cntl << endl;
return 0;
}