【2023上海大学春季赛】J. Juxtaposed brackets | 栈
题目链接
题目
递归定义集合 如下:
(1)
(2) 若 ,则 。
给定一个非空字符串 ,其中 只由两种字符(
和)
组成。
请你回答这个字符串是否属于 。
题目大意
如题目描述所示。
思路
首先我们发现 中的元素一定是匹配的括号序列,可以先用栈将非法的括号序列排查出去。
然后我们观察到题目中说到:
若 ,则 。
这就告诉我们一个事实,即:
若 ,则 。
我们利用括号的包含关系建树,假设 均为合法的括号序列,若 直接包含 ,即 或者 ,就使 成为 的父节点。
在这种规则下, 中元素产生的树一定是一个连通的二叉树。并且由集合 的定义,一个连通的二叉树对应的括号序列一定是 的子集。
那么具体怎样建树呢?
我们首先记录给每个括号取一个名字,并在左右括号都加以记录。每当我们遇到一个左括号 时,如果 的左边紧挨着一个左括号,那么他是 的父节点;如果 的左边紧挨着一个右括号,那么他是 的兄弟节点,他的父节点是 的父节点。
完成建树后,我们只需要判断整棵树是否连通、每个节点的子节点数量是否大于 即可。
为了方便的判断树是否连通,我使得全场第一个左括号以 作为其父节点,这样的话只需要判断 的子节点数量是否是 即可判断整棵树是否连通。
代码
#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;
}