牛客NC16660 [NOIP2004]FBI树(递归 + 二叉树后序遍历)
题目描述
我们可以把由 “0” 和 “1” 组成的字符串分为三类:
- 全“0”串称为 B串
- 全“1”串称为 I串
- 既含“0”又含“1”的串称为 F串
FBI 树是一种二叉树,它的结点类型也包括 F 结点、B 结点和 I 结点三种。由一个长度为 2N2^N2N 的 “01” 串 SSS 可以构造出一棵 FBI 树 TTT,递归构造的方法如下:
- TTT 的根结点为 RRR,其类型与串 SSS 的类型相同;
- 若串 SSS 的长度大于 1,将串 SSS 从中间分为等长的左右子串 S1S_1S1 和 S2S_2S2;
- 由左子串 S1S_1S1 构造 RRR 的左子树 T1T_1T1
- 由右子串 S2S_2S2 构造 RRR 的右子树 T2T_2T2
现在给定一个长度为 2N2^N2N 的 “01” 串,请用上述构造方法构造出一棵 FBI 树,并输出它的后序遍历序列。
后序遍历:先后序遍历左子树,再后序遍历右子树,最后访问根节点。
输入格式
- 第一行是一个整数 NNN,表示输入字符串的长度为 2N2^N2N。
- 第二行是一个长度为 2N2^N2N 的“01”串。
输出格式
输出一行字符串,表示 FBI 树的后序遍历序列。
样例输入
3
10001011
样例输出
IBFBBBFIBFIIIFF
数据范围与说明
- 对于 40% 的数据,N≤2N \leq 2N≤2
- 对于 100% 的数据,0≤N≤100 \leq N \leq 100≤N≤10
- 输入字符串保证长度为 2N2^N2N
提交链接
https://ac.nowcoder.com/acm/problem/16660
思路分析
🧠构建过程概括(DFS + 后序遍历)
我们使用 递归(DFS) 的方式模拟构造这棵 FBI 树。
每个结点对应一个字符串区间,对于每个区间:
- 先递归构造左子树(处理左半段)
- 再递归构造右子树(处理右半段)
- 然后判断当前区间的类型:
- 全为
'0'
:类型为'B'
- 全为
'1'
:类型为'I'
- 含
'0'
和'1'
:类型为'F'
- 全为
- 最后使用 后序遍历(左 → 右 → 根)输出所有结点的类型
树的大小分析
- 输入字符串的长度为 2n2^n2n,表示叶子结点数量为 2n2^n2n
- 构造出的 FBI 树是一棵 满二叉树
- 总结点数为:2n+1−12^{n+1} - 12n+1−1
例如:
n | 字符串长度 | 总节点数 |
---|---|---|
0 | 1 | 1 |
1 | 2 | 3 |
2 | 4 | 7 |
3 | 8 | 15 |
输出要求
输出 FBI 树的 后序遍历 序列:
- 顺序为:左子树 → 右子树 → 根节点
- 每个结点输出其对应类型(
F
/B
/I
)
✅ 整体结构划分
函数名 | 作用 |
---|---|
check(l, r) | 判断区间 [l, r] 的字符串属于 F / B / I 类型 |
dfs(l, r, id) | 构造以 str[l..r] 为区间的子树,存在数组 a 的下标 id 上 |
change(root) | 后序遍历二叉树并输出对应类型 |
参考代码
#include<bits/stdc++.h>
using namespace std;
vector<char>a(1000000);
int n;
string str;
char check(int l , int r) //判断字符串的构成 FBI 哪一种
{int zero = 0 , one = 0;for(int i = l; i <= r; i++){if(str[i] == '0')zero++;elseone++;}if(zero && one)return 'F';else if(zero)return 'B';elsereturn 'I';
}
void dfs(int l , int r , int id) //构造二叉树
{if(l == r){a[id] = check(l , r);return;}int mid = l + (r - l) / 2;dfs(l , mid , id * 2);dfs(mid + 1 , r , id * 2 + 1);a[id] = check(l , r);
}
void change(int root)
{if(a[root] == 0)return;change(2 * root);change(2 * root + 1);cout << a[root];
}
int main()
{cin >> n >> str;dfs(0 , str.size() - 1 , 1);change(1);return 0;
}