题目连接:https://codeforces.com/contest/1097/problem/C
可以将括号序列分成三类:
一,左括号个数大于右括号个数
这一类的一定会放在左边,所以要满足对于任意前缀左括号个数大于等于右括号个数,记左括号个数减右括号个数为\(i\)的此类序列有\(cntl_i\)个。
二,左括号个数等于右括号个数
这一类一定是自己跟自己配对,且要满足本身就是合法括号序列,记此类型的括号有\(C\)个。
三,左括号个数小于又括号个数
这一类括号一定放在右边,所以要满足对于任意后缀右括号个数大于等于左括号个数,记右括号个数减左括号个数为\(i\)的此类序列有\(cntr_i\)个。
那么答案是
\[\lfloor \frac{C}{2} \rfloor + \sum_{i=1}^{maxlength} min(cntl_i,cntr_i)\]
时间复杂度\(O(|S|)\),空间复杂度\(O(|S|)\)。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <iomanip>
#include <assert.h>
#include <fstream>
using namespace std;
int n,ans;
pair<int,int> cnt[500005];
string s;
int main()
{
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1;i <= n;i++)
{
cin >> s;
bool okf = true,okb = true;
int c = 0;
for (int j = 0;j < s.size();j++)
{
if (s[j] == '(')
c++;
else
c--;
if (c < 0)
okf = false;
}
int t = c;
c = 0;
for (int j = s.size() - 1;j >= 0;j--)
{
if (s[j] == ')')
c++;
else
c--;
if (c < 0)
okb = false;
}
if (okf && okb)
ans++;
else if (okf)
cnt[t].first++;
else if (okb)
cnt[c].second++;
}
ans >>= 1;
for (int i = 1;i <= 500000;i++)
ans += min(cnt[i].first,cnt[i].second);
cout << ans << endl;
return 0;
}