题目大意:一直一颗M叉树的先序遍历和后序遍历,求中序遍历的种数。(by the way,如果已知先序和中序,那么后序唯一,如果已知中序和后序,先序也是唯一的)
题目的关键在于“分治”的思想来DP,求得每个结点有多少个子节点,先把每个子节点的可能种数求出来,然后相乘,然后再乘以C(m,n),其中n为当前结点的子节点数目,所得到的结果就是以当前结点为根的中序遍历的种数。 顺便说一句,求C(m,n)的时候也可以用DP。
#include <iostream>
#include <cassert>
using namespace std;
int M;
char str1[27];
char str2[27];
int c[21][21];
int C(int m,int n)
{
assert( m<=20 );
assert( n<=m );
if( n == 0 || m == 0 || m == n )
return 1;
if( 2*n > m )
n = m-n;
if( c[m][n] != -1 )
return c[m][n];
else
{
c[m-1][n-1] = C( m-1, n-1 );
c[m-1][n] = C( m-1,n );
return c[m][n] = c[m-1][n-1] + c[m-1][n];
}
}
int Partition(const int beg1,const int end1,const int beg2,const int end2) //针对str的beg到end来分析
{
if( beg1 == end1 )
return 1;
int subroot_pos = beg1 + 1;
char* psubroot_instr2_old = str2+beg2-1;
int ret = 1;
int subtree_nums = 0;
for(; subroot_pos<=end1;)
{
char subroot = str1[subroot_pos];
char* psubroot_instr2_new = strchr(str2,subroot);
int len = psubroot_instr2_new - psubroot_instr2_old;
assert( len != 0 );
//ret = ret*Partition(subroot_pos,subroot_pos+len-1,psubroot_instr2_old-str2,psubroot_instr2_new-str2);
ret = ret*Partition(subroot_pos,subroot_pos+len-1,psubroot_instr2_new-str2-len+1,psubroot_instr2_new-str2);
psubroot_instr2_old = psubroot_instr2_new;
subroot_pos += len;
++subtree_nums;
}
ret = ret * C(M,subtree_nums);
return ret;
}
int main()
{
//freopen("in.txt","r",stdin);
while( true ){
scanf("%d",&M);
if( M == 0 )
break;
scanf("%s",str1);
scanf("%s",str2);
//init c
for(int i=0; i<=M; ++i)
for(int j=0; j<i; ++j)
c[i][j] = -1;
int res = Partition(0,strlen(str1)-1,0,strlen(str2)-1);
printf("%d\n",res);
}
return 0;
}