我们知道,对于一个二叉树,如果我们我们知道他的前(或后)序遍历和中序遍历,那就可以直接构造还原出完整的二叉树。中序遍历很重要。
那么给定一个树的前序遍历和后序遍历,能不能构造出这个树呢?一般是不行的,参考往期文章 https://www.cnblogs.com/ofnoname/p/16864200.html。既然不唯一,怎样确定有多少个符合条件的二叉树呢?

基本思路,是这样:
- 根结点在前序遍历的第一个元素,后序遍历的最后一个元素。他们应该相等,否则不计答案。简单起见我们假设答案存在,至少有一个可行的树。
- 除非就一个节点,答案为 1,否则我们可以将树划分为左子树和右子树。前序遍历的第二个元素就是左子树的根节点,找到他在后序遍历中的位置,就可以划分两个子树。
- 对于左子树和右子树,可以递归地解决相同的问题,他们的答案相乘就是总方案。
- 特殊的,如果前序遍历的第二个元素就是后序遍历的倒数第二个元素,这就说明只有一个子树,另一侧为空,这个子树可以是左子树或右子树,返回其答案乘以 2。

查询元素在遍历序列中的位置不要每次遍历,使用哈希表确保低复杂度。时间和空间复杂度是 \(O(n)\)。
cpp
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
unordered_map<char, int> mp;
string preorder, postorder;
int count(int pre_left, int pre_right, int post_left, int post_right) {
if (pre_left > pre_right || post_left > post_right) return 1; // 空树
if (pre_left == pre_right) return 1; // 只有一个节点,唯一方案
char left_root = preorder[pre_left + 1];
int left_root_pos = mp[left_root];
int left_size = left_root_pos - post_left + 1;
int right_size = post_right - left_size - post_left;
int left_ways = count(pre_left + 1, pre_left + left_size, post_left, left_root_pos);
int right_ways = count(pre_left + left_size + 1, pre_right, left_root_pos + 1, post_right - 1);
int result = left_ways * right_ways;
// 如果左子树和右子树不完全独立,需要乘以2
if (preorder[pre_left + 1] == postorder[post_right - 1]) {
result *= 2;
}
return result;
}
int main() {
cin >> preorder >> postorder;
for (int i = 0; i < preorder.size(); ++i) {
mp[postorder[i]] = i;
}
cout << count(0, preorder.size() - 1, 0, postorder.size() - 1) << endl;
return 0;
}