【2023上海大学春季赛】J. Juxtaposed brackets

1,150 阅读2分钟

【2023上海大学春季赛】J. Juxtaposed brackets | 栈

题目链接

Problem - J - Codeforces

题目

递归定义集合 BB 如下:

(1) ()B()\in B

(2) 若 x,yBx,y\in B,则 (x),(xy)B(x),(xy)\in B

给定一个非空字符串 SS,其中 SS 只由两种字符()组成。

请你回答这个字符串是否属于 BB

image.png

题目大意

如题目描述所示。

思路

首先我们发现 BB 中的元素一定是匹配的括号序列,可以先用栈将非法的括号序列排查出去。

然后我们观察到题目中说到:

若 x,yBx,y\in B,则 (x),(xy)B(x),(xy)\in B

这就告诉我们一个事实,即:

若 x,y,zBx,y,z\in B,则 (xyz)B(xyz)\notin B

我们利用括号的包含关系建树,假设 a,b,ca,b,c 均为合法的括号序列,若 aa 直接包含 bb,即 a=(b)a=(b) 或者 a=(bc)a=(bc),就使 aa 成为 bb 的父节点。

在这种规则下,BB 中元素产生的树一定是一个连通的二叉树。并且由集合 BB 的定义,一个连通的二叉树对应的括号序列一定是 BB 的子集。

那么具体怎样建树呢?

我们首先记录给每个括号取一个名字,并在左右括号都加以记录。每当我们遇到一个左括号 ii 时,如果 ii 的左边紧挨着一个左括号,那么他是 ii 的父节点;如果 ii 的左边紧挨着一个右括号,那么他是 ii 的兄弟节点,他的父节点是 ii 的父节点。

完成建树后,我们只需要判断整棵树是否连通、每个节点的子节点数量是否大于 22 即可。

为了方便的判断树是否连通,我使得全场第一个左括号以 00 作为其父节点,这样的话只需要判断 00 的子节点数量是否是 11 即可判断整棵树是否连通。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=300005;
char a[N];
int n,flag,id[N],bel[N],stk[N],t,tot,cnt[N];
int solve()
{
	for (int i=0;i<=n;++i) bel[i]=0,cnt[i]=0;
	scanf(" %s",a+1);
	n=strlen(a+1);
	flag=1;
	t=tot=0;
	for (int i=1;i<=n;++i)
	{
		if (a[i]=='(') id[i]=stk[++t]=++tot;
		else
		{
			if (t<1) return 0;
			id[i]=stk[t--];
		}
	}
	if (t!=0) return 0;
	for (int i=2;i<=n;++i)
	{
		if (a[i]!='(') continue;
		if (a[i-1]=='(') cnt[bel[id[i]]=id[i-1]]++;
		else cnt[bel[id[i]]=bel[id[i-1]]]++;
	}
	if (cnt[0]) return 0;
	for (int i=1;i<=n;++i)
		if (cnt[i]>2) return 0;
	return 1;
}
int main()
{
	int T;
	for (scanf("%d",&T);T--;) 
		if (solve()) printf("YES\n");
		else printf("NO\n");
	return 0;
	
}