括号序列
括号序列
题目描述
合法括号序列的定义是:
1.空序列是合法括号序列。
2.如果S是合法括号序列,那么(S)是合法括号序列。
3.如果A和B都是合法括号序列,那么AB是合法括号序列。
现在有一个括号序列,现在要计算一下它有多少非空子段是合法括号序列。
PS: 为了方便给出了5个样例, 但是丢在一个框框里
输入描述
一个字符串S
输出描述
有多少个非空子段是合法括号序列
输入样例
(
()
()()
(()
(())
输出样例
0
1
3
1
2
数据范围
\(1≤|S|≤10^6\)
原题链接
题目解析
今天的难度有点拉了,睡前看的题,醒来就会做了XD。群友说应该dp解决,我用递归解决理论上也可以dp解决,但是我没推出来状态转移方程。😦
- 对于每个答案而言,一个前括号一定和另一个固定的括号匹配,假设我们称一对合法序列\((S)\)为一组,那么一组序列贡献的答案为:
\[ans[(S)]=ans[S]+1
\]
(对于这个序列,既可以选子S,又可以选带括号的S)
- 在这一前提下,问题退化成了仅针对条件三的求解。
如果A和B都是合法括号序列,那么AB是合法括号序列。
对于一个没有外扩号的序列S而言,令序列S的合法序列数量为\(num\),那么答案的贡献为:
\[ans[S]=(num[S]+1)*num[S]/2
\]
是的,就是高斯求和的公式,由此,问题可以递归解决。
AC CODE
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef long long ll;
struct Node {
int index;
int num;
bool vis;
} node[N];
string s;
ll quary(int l,int r) { //在l到r这一段中有多少可能性
if(l>=r || l < 0 || r > s.size())
return 0;
ll ans = 0, cnt = 0;
for(int i = l ; i < r ; i++) {
if(node[i].num != -1) {
cnt++;
node[i].vis = 1;
ans += quary(i+1,node[i].num-1);
i = node[i].num;
}
}
ans += (cnt+1)*cnt/2;
return ans;
}
int main() {
ios::sync_with_stdio(0);
int n;
cin >> n;
while(cin >> s) {
stack<Node>stk;
for(int i = 0 ; i < s.size() ; i++) {
if(s[i]=='(')
stk.push({i,-1,0});
else if(stk.size()) {
int x = stk.top().index;
node[x] = stk.top();
stk.pop();
node[x].num = i;
} else node[i] = {i,-1,0};
}
while(stk.size()) {
int x = stk.top().index;
node[x] = stk.top();
stk.pop();
}
ll ans = 0;
for(int i = 0 ; i < s.size() ; i++) {
if(node[i].num != -1) {
int l = i;
while(node[i].num != -1 && i < s.size()) {
i = node[i].num+1;
}
ans += quary(l,i);
}
}
cout << ans << endl;
}
}