[codevs1029]遍历问题
[codevs1029]遍历问题
试题描述
我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历。然而给定一棵二叉树的前序和后序,你却不能确定其中序遍历序列,考虑如下图中的几棵二叉树:
所有这些二叉树都有着相同的前序遍历和后序遍历,但中序遍历却不相同。
输入
输入文件共2行,第一行表示该树的前序遍历结果,第二行表示该树的后序遍历结果。输入的字符集合为{a-z},长度不超过26。
输出
输出文件只包含一个不超过长整型的整数,表示可能的中序遍历序列的总数。
输入示例
abc
cba
输出示例
4
数据规模及约定
见“输入”
题解
不难发现一颗子树所对应的前序、后序遍历都是一段连续的子串,并且前序遍历子串中最靠前的字符一定等于后序遍历中最靠后的那个字符。于是就可以 dp 了,设 f(l, r, x, y) 表示对于前序遍历字符串中 [l, r] 这个子串和后序遍历中 [x, y] 这个子串的二叉树的数目,转移不妨读者自行思考。注:[l, r] 和 [x, y] 的长度一定相等,所以可以省掉一维,不过这题的数据也无所谓。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 30 int n, f[maxn][maxn][maxn]; char be[maxn], af[maxn]; int main() { scanf("%s%s", be + 1, af + 1); n = strlen(be + 1); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) if(be[i] == af[j]) f[i][i][j] = 1; for(int i = 1; i <= n + 1; i++) for(int j = 0; j <= n + 1; j++) f[i][i-1][j] = 1; for(int len = 2; len <= n; len++) for(int l = 1; l <= n; l++) { int r = l + len - 1; for(int x = 1; x <= n; x++) { int y = x + len - 1; f[l][r][x] = 0; if(be[l] != af[y]) continue; for(int k = 0; k < len; k++) { f[l][r][x] += f[l+1][l+k][x] * f[l+k+1][r][x+k]; // printf("to [%d, %d][%d, %d] and [%d, %d][%d, %d]\n", l+1, l+k, x, x+k-1, l+k+1, r, x+k, x+r-l-1); } // printf("[%d, %d] and [%d, %d]: %d\n", l, r, x, y, f[l][r][x]); } } printf("%d\n", f[1][n][1]); return 0; }