括号序列

括号序列

题目描述

合法括号序列的定义是:

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;
	}
}
posted @ 2022-04-08 10:41  seekerHeron  阅读(734)  评论(0)    收藏  举报