题目描述
难度分:1500
输入n(1≤n≤3×105)长为n的括号字符串数组s,字符串长度之和≤3×105。
输出有多少个(i,j)使得s[i]+s[j]是合法括号字符串。
注意i可以等于j,也可以大于j。
输入样例1
7
)())
)
((
((
(
)
)
输出样例1
2
输入样例2
4
(
((
(((
(())
输出样例2
4
算法
这个题目和昨天的几乎一样,可以用同一个贪心策略。
贪心
每个字符串从左往右遍历,按照括号匹配的思路计算最后剩下的左括号“净含量”left和右括号“净含量”right。观察之后可以发现如下规律:
- 如果left>0和right>0同时成立,这个字符串就应该被抛弃掉,不可能存在能和它匹配出合法括号串的另一个字符串。
- 如果left>0,就把它记录到哈希表lcnt中(lcnt的映射关系为“左括号净含量 → 这个净含量的字符串数目”)。
- 如果right>0,就把它记录到哈希表rcnt中(rcnt的映射关系为“右括号净含量 → 这个净含量的字符串数目”)。
- 如果left=right=0,记录满足这个的字符串数目zcnt,这样的字符串可以两两配对,一共产生zcnt2个合法括号串。
接下来就是让左括号“净含量”与右括号“净含量”相等的串进行配对,对于一个在lcnt和rcnt都存在的“净含量”x,它对答案的贡献为lcnt[x]×rcnt[x],把这些x的贡献都累加起来就是答案。
复杂度分析
时间复杂度
遍历了n个字符串一次,对于每个字符串,需要遍历它的每个字符,而字符串长度的总和也是O(n)的,因此遍历的时间复杂度为O(n)。
之后要遍历哈希表lcnt,对每个key都要检查其是否也在rcnt中,从而计算最大配对数。在最差情况下每个字符串的左括号净含量都不同,有O(n)级别的key数量,因此遍历的时间复杂度为O(n)。
综上,整个算法的时间复杂度为O(n)。
空间复杂度
开辟了lcnt和rcnt两个哈希表的空间,在最差情况下这两个哈希表会存O(n)级别的键值对,因此算法整体的额外空间复杂度为O(n)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
int n;
int main() {
scanf("%d", &n);
string s;
int zcnt = 0;
unordered_map<int, int> lcnt, rcnt;
for(int i = 0; i < n; i++) {
cin >> s;
int left = 0, right = 0;
for(char c: s) {
if(c == '(') {
left++;
}else {
if(left) left--;
else right++;
}
}
if(left && right) continue;
if(left) lcnt[left]++;
if(right) rcnt[right]++;
if(left == 0 && right == 0) zcnt++;
}
LL ans = (LL)zcnt*zcnt;
for(auto&[k, v]: lcnt) {
if(rcnt.find(k) != rcnt.end()) {
ans += (LL)v*rcnt[k];
}
}
printf("%lld\n", ans);
return 0;
}