D. A Simple RBS Problem time limit per test2 secon...

创建于:2026年3月29日

使用 GPT-5.4 Thinking 回答 Chat01

问题

D. A Simple RBS Problem
time limit per test2 seconds
memory limit per test256 megabytes

A bracket sequence is called regular if it is possible to obtain a correct arithmetic expression by inserting characters +
and 1
into this sequence. For example, sequences (())(), () and (()(())) are regular, while )(, (() and (()))( are not. Let's call a regular bracket sequence RBS.

You can perform the following operation on any RBS string b

Choose any two disjoint, non empty substrings∗
of b
which are RBS and swap the two substrings.
More formally, let m
be the length of b
. You can perform the following operation on b
:

Choose indices 1≤l1<r1<l2<r2≤m
such that the substrings b[l1..r1]
and b[l2..r2]
are both RBS and swap these substrings. The string b
becomes b1…bl1−1bl2bl2+1…br2br1+1…bl2−1bl1bl1+1…br1br2+1…bm
.
You are given two strings s
and t
of length n
(n
is even) such that both s
and t
are RBS.

Determine whether you can transform s
to t
using the above operation any number of times.


A string a
is a substring of a string b
if a
can be obtained from b
by the deletion of several (possibly, zero or all) characters from the beginning and several (possibly, zero or all) characters from the end.

Input
Each test contains multiple test cases. The first line contains the number of test cases t
(1≤t≤104
). The description of the test cases follows.

The first line of each test case contains an integer n
(4≤n≤5⋅105
, n
is even).

The second line of each test case contains a string s
of length n
such that s
is an RBS.

The third line of each test case contains a string t
of length n
such that t
is an RBS.

It is guaranteed that the sum of n
over all test cases does not exceed 5⋅105
.

Output
For each test case, print "YES" if s
can be transformed to t
, and "NO" otherwise.

You can output the answer in any case (upper or lower). For example, the strings "yEs", "yes", "Yes", and "YES" will be recognized as positive responses.

Example
InputCopy
5
4
()()
()()
4
()()
(())
6
(())()
()(())
8
(()()())
()()(())
8
()()(())
()(()())
OutputCopy
YES
NO
YES
NO
YES
Note
In the first test case, the two strings are already same.

In the third test case, we can choose l1=1,r1=4,l2=5,r2=6
and transform s
to t
.

Here is a visualization of the operation performed in this case — give code for this in java and think of all edge cases and see all test cases and give correct solution like a senior coder

(())()→()(())

思考

I’m working through the invariant behind the swap operation, then I’ll turn it into a linear-time check and a clean Java implementation.

Thought for 8s

Python

python
from collections import deque,defaultdict def rbs(s): bal=0 for c in s: bal+=1 if c=='(' else -1 if bal<0:return False return bal==0 def subs(s): n=len(s) res=[] for l1 in range(n): for r1 in range(l1+1,n+1): a=s[l1:r1] if not rbs(a): continue for l2 in range(r1,n): for r2 in range(l2+1,n+1): b=s[l2:r2] if not rbs(b): continue t=s[:l1]+b+s[r1:l2]+a+s[r2:] res.append((l1,r1,l2,r2,t)) return res def classes(n): from itertools import product arr=[''.join(p) for p in product('()', repeat=n) if rbs(''.join(p))] seen=set() cl=[] graph=defaultdict(set) for s in arr: for *_,t in subs(s): graph[s].add(t) graph[t].add(s) for s in arr: if s in seen: continue q=[s]; seen.add(s); comp=[] while q: x=q.pop(); comp.append(x) for y in graph[x]: if y not in seen: seen.add(y); q.append(y) cl.append(sorted(comp)) return arr,cl for n in [2,4,6,8]: arr,cl=classes(n) print("n",n,"count",len(arr),"classes",len(cl),[len(c) for c in cl]) for c in cl: print(c) print()

Python

python
s="()()(())" l1,r1,l2,r2=2,4,5,7 t=s[:l1]+s[l2:r2]+s[r1:l2]+s[l1:r1]+s[r2:] print(t)

Python

python
from collections import deque def path(start,target): q=deque([start]); par={start:None} move={} while q: s=q.popleft() if s==target: break n=len(s) for l1 in range(n): for r1 in range(l1+1,n+1): if not rbs(s[l1:r1]): continue for l2 in range(r1,n): for r2 in range(l2+1,n+1): if not rbs(s[l2:r2]): continue t=s[:l1]+s[l2:r2]+s[r1:l2]+s[l1:r1]+s[r2:] if t not in par: par[t]=s; move[t]=(l1,r1,l2,r2) q.append(t) if target not in

Python

python
def count_adj(s): return sum(1 for i in range(len(s)-1) if s[i:i+2]=='()') for n in [2,4,6,8,10]: arr=[''.join(p) for p in __import__('itertools').product('()', repeat=n) if rbs(''.join(p))] # compute classes from collections import defaultdict groups=defaultdict(list) for s in arr: groups[count_adj(s)].append(s) # BFS classes from before maybe expensive for 10 maybe skip print(n, {k:len(v) for k,v in groups.items()})

Python

python
from itertools import product def comps(n): arr=[''.join(p) for p in product('()', repeat=n) if rbs(''.join(p))] # build graph maybe manageable for 10? 42 states yes graph=defaultdict(set) for s in arr: for *_,t in subs(s): graph[s].add(t); graph[t].add(s) seen=set(); cls=[] for s in arr: if s in seen: continue q=[s]; seen.add(s); comp=[] while q: x=q.pop(); comp.append(x) for y in graph[x]: if y not in seen: seen.add(y); q.append(y) cls.append(comp) return cls cls10=comps(10) print(len(cls10), sorted(len(c)

Python

python
for c in sorted(cls10, key=lambda x:(len(x), min(x))): print("size",len(c),"peaks",count_adj(c[0]), sorted(c)) print()

Python

python
def leaf_depths(s): st=[]; depths=[] depth=0 for i,c in enumerate(s): if c=='(': depth+=1 st.append(i) else: j=st.pop() if i==j+1: depths.append(depth) depth-=1 return tuple(sorted(depths, reverse=True)) for c in sorted(cls10, key=lambda x:(count_adj(x[0]), len(x), min(x))): rep=min(c) print(rep, "peaks", count_adj(rep), "leafdepths", leaf_depths(rep), "size", len(c))

Python

python
for n in [8]: arr,cl=classes(n) for c in sorted(cl, key=lambda x:(len(x),min(x))): rep=min(c) print(rep, len(c), leaf_depths(rep)) print(sorted(c)) print()

Python

python
for s in ["()()(())","()(()())","(()())()","(())()()"]: print(s, leaf_depths(s))

Python

python
for c in classes(8)[1]: print({leaf_depths(s) for s in c}, c)

Python

python
class Node: def __init__(self): self.children=[] def parse_forest(s): stack=[[]] # list of forests, stack top is current children list for c in s: if c=='(': stack.append([]) else: ch=stack.pop() node=Node(); node.children=ch stack[-1].append(node) return stack[0] def leaf_rounds_forest(forest): rounds=[] cur=forest while cur: # count leaves in current forest def count_leaves(nodes): return sum(1 if not nd.children else count_leaves(nd.children) for nd in nodes) rounds.append(count_leaves(cur)) # prune leaves def prune(nodes): new=[] for nd in nodes: if nd.children: nd2=Node(); nd2.children=prune(nd.children) new.append(nd2) return new cur=prune(cur) return rounds for c in classes(8)[1]: rr={tuple(leaf_rounds_forest(parse_forest(s))) for s in c} print(c, rr)

Python

python
def peak_heights(s): bal=0 res=[] for i,c in enumerate(s): if c=='(': bal+=1 if i+1<len(s) and s[i+1]==')': res.append(bal) else: bal-=1 return tuple(res) for rep in ['(()())(())','((()())())','((()()()))']: print(rep, peak_heights(rep))

Python

python
def signature(s): forest=parse_forest(s) sig=[] cur=forest while cur: counts=[] def process(nodes): new=[] for nd in nodes: leafc=sum(1 for ch in nd.children if not ch.children) intc=[ch for ch in nd.children if ch.children] if nd.children: counts.append(leafc) nd2=Node(); nd2.children=intc # shallow okay new.append(nd2) return new # include super-root root_leafc=sum(1 for ch in cur if not ch.children) root_int=[ch for ch in cur if ch.children] counts.append(root_leafc) sig.append(tuple(sorted([x for x in counts if x>0], reverse=True))) cur=process(root_int) return tuple(sig) for c in classes(8)[1]: print(sorted(c), {signature(s) for s in c})

Python

python
def match_arr(s): st=[] m=[None]*len(s) for i,c in enumerate(s): if c=='(': st.append(i) else: j=st.pop(); m[i]=j; m[j]=i return m def inv(s): m=match_arr(s) h=0 n=len(s) while h<n//2 and m[h]==n-1-h: h+=1 p=count_adj(s) return p,h for n in [8,10]: arr,cl=classes(n) ok=True for c in cl: vals={inv(s) for s in c} if len(vals)!=1: ok=False print("class not constant",c,vals) # compare unique invs count with class count print("n",n,"ok",ok,"class count",len(cl),"inv count",len({inv(s) for s in arr})) from collections import defaultdict grp=defaultdict(list) for s in arr: grp[inv(s)].append(s) # check each inv group connected (equal to some class) for key,g in grp.items(): # see if all g in same class cs=[c for c in cl if g[0] in c][0] if set(g)!=set(cs): print("mismatch",key,set(g)-set(cs),set(cs)-set(g)) ok=False break print("final",ok)

Python

python
def do(s,l1,r1,l2,r2): return s[:l1]+s[l2:r2]+s[r1:l2]+s[l1:r1]+s[r2:] s="()(()())" # prefix first leaf 0..2, inner leaf maybe positions 3..5? let's enumerate RBS substrings and result n=len(s) for l1 in range(n): for r1 in range(l1+1,n+1): if not rbs(s[l1:r1]): continue for l2 in range(r1,n): for r2 in range(l2+1,n+1): if not rbs(s[l2:r2]): continue t=do(s,l1,r1,l2,r2) if t=="()()(())": print((l1,r1,l2,r2), s[l1:r1], s[l2:r2])

思考

Generalizing M2 to handle 1-peak chains

Step 1: For a large component A with q>1 peaks and a 1-peak chain B, we choose a non-primitive 2-peak substring (U, V) from A. We swap B with this substring, resulting in two 1-peak chains from U and V outside, while A's peak count reduces by 1. This makes the operation generalizable!

Simplifying to the canonical form

Step 1: By swapping a leaf inside a big component and reducing its peak count, we increase the number of top-level 1-peak chains. Repeating this step eventually results in all components being 1-peak chains. Step 2: Pairwise combine these chains into one large chain, following the base p=2 rule, resulting in the canonical form.

Thought for 24m 13s

回答

The key is that this operation preserves exactly two things:

  1. cnt = the number of occurrences of "()"
    This is the number of leaves in the bracket-tree.

  2. outer = how many outermost pairs are forced
    In other words, how many times you can peel the whole string as
    s = "(" + mid + ")"
    and still the first '(' matches the last ')'.

Two RBS strings are transformable iff these two values are equal.


Why this is true

Think of an RBS as an ordered forest:

  • each matched pair of brackets is a node,
  • children of a node are the RBS blocks directly inside it,
  • top-level blocks are children of a virtual root.

Then:

  • any RBS substring is exactly a consecutive block of siblings,
  • the operation swaps two such consecutive sibling blocks.

Invariant 1: number of "()"

A node with no children is exactly a substring "()".
Swapping sibling blocks does not create or destroy leaf nodes, it only moves whole subtrees around.

So the count of "()" is invariant.


Invariant 2: number of forced outer pairs

If the whole string is primitive, meaning the first '(' matches the last ')', then no valid swapped substring can touch outside that pair unless it is the whole string, which is impossible because the two substrings must be disjoint and non-empty.

So that outermost pair is untouchable.

Apply the same argument recursively inside it.
Hence the number of such forced outer layers is invariant.

A very easy way to compute it:

  • build the matching bracket array,
  • count how many initial positions i satisfy match[i] == n-1-i.

Sufficiency

After removing those forced outer pairs from both strings, the remaining parts have:

  • no common forced outer pair,
  • the same number of leaves ("()").

All such forests are equivalent: you can repeatedly expose leaves at the top level and finally reduce everything to the same canonical form:

  • several top-level "()",
  • and one fully nested chain at the end.

So equal (cnt, outer) means reachable.


What to compute

For each string:

  • cnt = number of i with s[i]=='(' and s[i+1]==')'
  • outer = number of leading matched pairs nesting all the way to the end

Answer is YES iff both values match.


Java solution

java
import java.io.*; import java.util.*; public class Main { // Fast input static class FastScanner { private final InputStream in; private final byte[] buffer = new byte[1 << 16]; private int ptr = 0, len = 0; FastScanner(InputStream is) { in = is; } private int read() throws IOException { if (ptr >= len) { len = in.read(buffer); ptr = 0; if (len <= 0) return -1; } return buffer[ptr++]; } String next() throws IOException { StringBuilder sb = new StringBuilder(); int c; do { c = read(); } while (c <= ' ' && c != -1); while (c > ' ') { sb.append((char) c); c = read(); } return sb.toString(); } int nextInt() throws IOException { int c; do { c = read(); } while (c <= ' ' && c != -1); int sign = 1; if (c == '-') { sign = -1; c = read(); } int val = 0; while (c > ' ') { val = val * 10 + (c - '0'); c = read(); } return val * sign; } } static class Info { int peaks; // number of occurrences of "()" int outer; // number of forced outer layers Info(int peaks, int outer) { this.peaks = peaks; this.outer = outer; } } static Info analyze(String s) { int n = s.length(); // 1) count peaks = number of "()" int peaks = 0; for (int i = 0; i + 1 < n; i++) { if (s.charAt(i) == '(' && s.charAt(i + 1) == ')') { peaks++; } } // 2) build matching brackets int[] match = new int[n]; int[] stack = new int[n]; int top = 0; for (int i = 0; i < n; i++) { char c = s.charAt(i); if (c == '(') { stack[top++] = i; } else { int j = stack[--top]; match[i] = j; match[j] = i; } } // 3) count forced outer layers int outer = 0; while (outer < n / 2 && match[outer] == n - 1 - outer) { outer++; } return new Info(peaks, outer); } public static void main(String[] args) throws Exception { FastScanner fs = new FastScanner(System.in); StringBuilder out = new StringBuilder(); int T = fs.nextInt(); while (T-- > 0) { int n = fs.nextInt(); String s = fs.next(); String t = fs.next(); Info a = analyze(s); Info b = analyze(t); if (a.peaks == b.peaks && a.outer == b.outer) { out.append("YES\n"); } else { out.append("NO\n"); } } System.out.print(out); } }

Edge cases this handles

  • already equal strings
  • fully nested strings like (((())))
  • fully flat strings like ()()()()
  • primitive vs non-primitive mismatch
  • same number of "()" but different forced outer depth
  • large total input size up to 5e5

Complexity

For each test case:

  • O(n) time
  • O(n) memory

Total over all test cases: O(sum n).

If you want, I can also give a shorter competitive version without comments.

分享这个问答