我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。
FBI树是一种二叉树1,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2^N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:
- T的根结点为R,其类型与串S的类型相同;
- 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。
现在给定一个长度为2^N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历2序列。
输入格式:
输入的第一行是一个整数N(0<=N<=10),第二行是一个长度为2^N的“01”串。
输出格式:
输出包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。
提示:
NOIP2004普及组第三题
限制:
每个测试点1s
样例 1 :
输入:
3
10001011
输出:
IBFBBBFIBFIIIFF
样例 2 :
输入:
0
0
输出:
B
//题意及思路:就是建立一颗二叉树,然后输出后续遍历的结果;唯一特殊的是,建立二叉树的规则:把父结点的字符串等分两份,分别给其左右儿子,递归执行上述过程,直到父结点的字符串不能二分为止
以下是我根据样例手动建的一颗二叉树
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
const int INF = 0x3fffffff;
typedef long long ll;
struct node { //每个结点的结构体
char id;
string ss;
node* lc, *rc;
};
char getid(string s) { //判断字符串的类型(用string类的find函数很方便)
if (s.find('0') == s.npos) return 'I';
else if (s.find('1') == s.npos) return 'B';
return 'F';
}
void gettree(node*& root) { //建树(注意是指针的引用)
if (root->ss.size() > 1) { //字符串能二分
node *temp = new node; //分给左儿子的部分
temp->ss = root->ss.substr(0, root->ss.size() / 2);
temp->id = getid(temp->ss);
temp->lc = temp->rc = NULL;
root->lc = temp;
temp = new node; //分给右儿子的部分
temp->ss = root->ss.substr(root->ss.size() / 2, root->ss.size() / 2);
temp->id = getid(temp->ss);
temp->lc = temp->rc = NULL;
root->rc = temp;
gettree(root->lc); //递归建树
gettree(root->rc);
}
}
void postravel(node* &root) { //后序遍历
if (!root) return;
postravel(root->lc);
postravel(root->rc);
cout << root->id; //输出每个结点的id
}
int main() {
int n;
string s;
cin >> n >> s;
node* root = new node; //初始化树的根节点
root->id = getid(s);
root->ss = s;
root->lc = root->rc = NULL;
gettree(root); //以root为根结点建树
postravel(root); //以root为根结点后序遍历
return 0;
}
//另一种解法:还可以根据二叉树的性质:如果父节点的下标是k,则其左儿子的下标是2 * k, 右儿子的下标是2 * k + 1;利用这个性质,可以建一个数组,储存每个结点的字符串类型,然后输出即可。在此就不上代码了,有兴趣的读者可自行实现