括号序列字典序、第k小括号序列、下一个括号序列

括号序列字典序、第k小括号序列、下一个括号序列

认为左括号字典序小于右括号
n 表示序列总长而不是括号对数

合法括号序列计数(共 k 种不同括号):

(n0.5n)0.5n+1k0.5n

给定括号序列求他的字典序

要求出比 s 字典序小的合法括号序列个数,枚举 i,表示对于 1j<i,都有 pj=sj,而对于 i,有 pi<si
其中 p 就是那个比 s 小的合法括号序列,同时可以知道这里 si 一定是右括号
设当前 [1,i] 种左括号比右括号多了 k 个,那么 [i+1,n] 中右括号就应该比左括号多了 k 个(准确说是有 k 个不匹配的,并且没有不匹配的左括号)
f(i,j) 表示长度为 i 的括号序列,没有不匹配的左括号,不匹配的右括号有 j 个的方案数
枚举序列第一个填什么,可以得到转移 f(i,j)=f(i1,j1)+f(i1,j+1)
这样可以 O(n2) 解决了

另外,如果有多种括号,方法类似,变成对于每个 si 考虑所欲比他小的字符进行计算(比如上面比右括号小的只有左括号,就只计算了左括号的情况)

inline void pre(int n){
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		f[i][0]=f[i-1][1];
		for(int j=1;j<=n;j++) f[i][j]=f[i-1][j-1]+f[i-1][j+1];
	}
}
inline BigInt rank(char *s){
	int n=std::strlen(s+1);
	pre(n);
	BigInt ans;ans.clear();
	int num=0;
	for(int i=1;i<=n;i++){
		if(s[i]==')') ans+=f[n-i][num+1],num--;
		else num++;
	}
	return ans+1;
}

给定字典序求对应的括号序列

求字典序第 k 小的合法括号序列
还是从前往后考虑,看这个位置能不能填右括号,如果填了右括号根据上面的理论需要要求 k>f(ni,num+1)
其中 num 是当前左括号比右括号多的数量,因为若填了右括号会产生 f(ni,num+1) 个比它小的括号序列 p
如果不能填右括号,还满足填了左括号以后不会使得后面都填右括号也不能使得序列合法,也就是 num<n2,那么就填左括号
否则,填右括号,给 k 减去 f(ni,num+1)

inline void kth(int n,BigInt k,char *s){
	pre(n);
	int num=0;
	for(int i=1;i<=n;i++){
		if(num+1<=(n>>1)&&f[n-i][num+1]>=k) s[i]='(',num++;
		else{
			s[i]=')';
			if(num+1<=(n>>1)) k-=f[n-i][num+1];
			num--;
		}
	}
}

对于有多种括号的情况,给出只有两种括号的代码,来自:https://cp-algorithms.com/combinatorics/bracket_sequences.html

string kth_balanced2(int n, int k) {
    vector<vector<int>> d(2*n+1, vector<int>(n+1, 0));
    d[0][0] = 1;
    for (int i = 1; i <= 2*n; i++) {
        d[i][0] = d[i-1][1];
        for (int j = 1; j < n; j++)
            d[i][j] = d[i-1][j-1] + d[i-1][j+1];
        d[i][n] = d[i-1][n-1];
    }

    string ans;
    int shift, depth = 0;

    stack<char> st;
    for (int i = 0; i < 2*n; i++) {

        // '('
        shift = ((2*n-i-1-depth-1) / 2);
        if (shift >= 0 && depth + 1 <= n) {
            int cnt = d[2*n-i-1][depth+1] << shift;
            if (cnt >= k) {
                ans += '(';
                st.push('(');
                depth++;
                continue;
            }
            k -= cnt;
        }

        // ')'
        shift = ((2*n-i-1-depth+1) / 2);
        if (shift >= 0 && depth && st.top() == '(') {
            int cnt = d[2*n-i-1][depth-1] << shift;
            if (cnt >= k) {
                ans += ')';
                st.pop();
                depth--;
                continue;
            }
            k -= cnt;
        }

        // '['
        shift = ((2*n-i-1-depth-1) / 2);
        if (shift >= 0 && depth + 1 <= n) {
            int cnt = d[2*n-i-1][depth+1] << shift;
            if (cnt >= k) {
                ans += '[';
                st.push('[');
                depth++;
                continue;
            }
            k -= cnt;
        }

        // ']'
        ans += ']';
        st.pop();
        depth--;
    }
    return ans;
}

求字典序的下一个括号序列

找到一个最大的 i,满足:

  • si 是左括号
  • [1,i1] 种左括号的数量 严格大于 右括号的数量

然后将 si 变成右括号,并重构序列 [i+1,n] 的部分
若改变 si 后,[1,i] 种左括号比右括号多了 k 个,为了让后面的字典序尽可能小,让最后 k 个都是右括号,而剩下的就用 (((()))) 的形式填充即可
复杂度 O(n)

posted @   suxxsfe  阅读(298)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
历史上的今天:
2020-03-09 行列式学习笔记

This blog has running: 1859 days 13 hours 40 minutes 28 seconds

Copyright © 2025 suxxsfe
Powered by .NET 9.0 on Kubernetes
点击右上角即可分享
微信分享提示