不妨设当 si 变成右括号后,s[1,i] 中左括号比右括号多了 k 个。那么我们就让 s 的最后 k 个字符变成右括号,而 s[i+1,∣s∣−k] 则用 ((…(())…)) 的形式填充即可,因为这样填充的字典序最小。
该算法的时间复杂度是 O(n)。
参考实现
bool next_balanced_sequence(string& s) { int n = s.size(); int depth = 0; for (int i = n - 1; i >= 0; i--) { if (s[i] == '(') depth--; else depth++; if (s[i] == '(' && depth > 0) { depth--; int open = (n - i - 1 - depth) / 2; int close = n - i - 1 - open; string next = s.substr(0, i) + ')' + string(open, '(') + string(close, ')'); s.swap(next); return true; } } return false;}
字典序计算
给出合法的括号序列 s,我们要求出它的字典序排名。
考虑求出字典序比 s 小的括号序列 p 的个数。
不妨设 pi<si 且 ∀1≤j<i,pj=si。显然 pi 是左括号而 si 是右括号。枚举 i(满足 si 为右括号),假设 p[1,i] 中左括号比右括号多 k 个,那么相当于我们要统计长度为 ∣s∣−i 且存在 k 个未匹配的右括号且不存在未匹配的左括号的括号序列的个数。
不妨设 f(i,j) 表示长度为 i 且存在 j 个未匹配的右括号且不存在未匹配的左括号的括号序列的个数。
通过枚举括号序列第一个字符是什么,可以得到 f 的转移:f(i,j)=f(i−1,j−1)+f(i−1,j+1)。初始时 f(0,0)=1。其实 f 是 OEIS - A053121。