首页 > 试题广场 >

Pre-Post

[编程题]Pre-Post
  • 热度指数:2223 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
We are all familiar with pre-order, in-order and post-order traversals of binary trees. A common problem in data structure classes is to find the pre-order traversal of a binary tree when given the in-order and post-order traversals. Alternatively, you can find the post-order traversal when given the in-order and pre-order. However, in general you cannot determine the in-order traversal of a tree when given its pre-order and post-order traversals. Consider the four binary trees below:
All of these trees have the same pre-order and post-order traversals. This phenomenon is not restricted to binary trees, but holds for general m-ary trees as well.

输入描述:
Input will consist of multiple problem instances. Each instance will consist of a line of the form m s1 s2, indicating that the trees are m-ary trees, s1 is the pre-order traversal and s2 is the post-order traversal.All traversal strings will consist of lowercase alphabetic characters. For all input instances, 1 <= m <= 20 and the length of s1 and s2 will be between 1 and 26 inclusive. If the length of s1 is k (which is the same as the length of s2, of course), the first k letters of the alphabet will be used in the strings. An input line of 0 will terminate the input.


输出描述:
For each problem instance, you should output one line containing the number of possible trees which would result in the pre-order and post-order traversals for the instance. All output values will be within the range of a 32-bit signed integer. For each problem instance, you are guaranteed that there is at least one tree with the given pre-order and post-order traversals.
示例1

输入

2 abc cba
2 abc bca
10 abc bca
13 abejkcfghid jkebfghicda

输出

4
1
45
207352860
import java.util.*;
public class Main{
    private static long num=1;
    private static long numArr[];

    private static void initArr(){
        numArr=new long[21];
        numArr[0]=1;
        for(int i=1;i<21;i++){
            numArr[i]=numArr[i-1]*i;
        }
    }

    private static long CaculateCom(int subNum,int n){
        return numArr[n]/(numArr[n-subNum]*numArr[subNum]);
    }

    public static void main(String[] args){
        Scanner scanner=new Scanner(System.in);
        initArr();
        while(scanner.hasNext()){
            int n=scanner.nextInt();
            String preOrder=scanner.next();
            String postOrder=scanner.next();
            num=1;
            CaculateTree(n,preOrder,postOrder);
            System.out.println(num);
        }
    }

    private static void CaculateTree(int n,String preOrder,String postOrder){
        int len=preOrder.length();
        if(len==1){
            return;
        }
        int count=0;
        preOrder=preOrder.substring(1);
        postOrder=postOrder.substring(0,len-1);
        while(!"".equals(preOrder)){
            int index=postOrder.indexOf(preOrder.charAt(0))+1;
            String newPre=preOrder.substring(0,index);
            String newPost=postOrder.substring(0,index);
            preOrder=preOrder.substring(index);
            postOrder=postOrder.substring(index);
            count++;
            CaculateTree(n,newPre,newPost);
        }
        num*=CaculateCom(count,n);
    }

}  

发表于 2018-09-15 21:21:18 回复(0)
#include <string>
#include <tuple>
#include <list>
#include <cstdio>
// 求n的阶乘
long long factorial(int n){
    long long r = 1;
    for (int i = 1; i <= n; i++){
        r *= i;
    }
    return r;
}
// 求 n, m 的组合 C(n, m)
// 利用 C(n, m) == C(n, n - m) 的特点,计算容易的
long long combination(int n, int m){
    int max = m > (n - m) ? m : (n - m);
    long long numerator = 1;
    for (int i = max + 1; i <= n; i++){
        numerator *= i;
    }
    return numerator / factorial(n - max);
}// 重命名类型,类似于 typedef 作用
using PrePost = std::tuple<std::string, std::string>;
// 给出一棵树的前序+后序,利用最上面注释的原理 // 把每棵子树的前序+后序切分出来
std::list<PrePost> splitSubTrees(std::string const& pre, std::string const& post){
    std::list<PrePost> list{};
    size_t preIdx = 1;
    size_t lastPost = 0;
    while (preIdx < pre.size()){
        char rootValue = pre[preIdx];
        size_t postIdx = post.find(rootValue);
        int countOfNode = postIdx - lastPost + 1;
        list.emplace_back(std::make_tuple( pre.substr(preIdx, countOfNode), post.substr(lastPost, countOfNode) ) );
        preIdx += countOfNode;
        lastPost += countOfNode;
    }
    return list;
}
// 递归的求解每一层的可能性,直到树中只剩一个或者零个结点
long long calculateNumOfPossibilities(int m, std::string const& pre, std::string const& post){
    if (pre.size() == 0 || pre.size() == 1){
        return 1;
    }
    std::list<PrePost> subTrees = splitSubTrees(pre, post);
    long long result = combination(m, subTrees.size());
    for (PrePost const& prePost : subTrees){
        result *= calculateNumOfPossibilities(m,std::get<0>(prePost), std::get<1>(prePost));
    }
    return result;
}
int main(){
    int m;
    char pre[30];
    char post[30];
    while (scanf("%d %s %s", &m, pre, post) != EOF){
        printf("%lld\n", calculateNumOfPossibilities(m, pre, post));
    }
    return 0;
}

发表于 2020-09-04 13:27:56 回复(0)
/*pre post
不要被吓死,要分析问题的本质,本质就是找到每一个根节点的子树
因为是m叉树,所以可以得到当前根节点的子树有多少组合方式,再
去到每一颗子树,如此递归直到找到所有叶子结点
比如    abejkcfghid     jkebfghicda
找到先序序列的第一个节点和后序序列的最后一个节点,这个节点就是当前的根节点
当前的根→a bejk  cfghi  d         jkeb  fghic  d a
                    ↑        ↑       ↑              ↑        ↑  ↑
                                找到3个区间,说明这一层有三颗子树
遍历后序序列找到先序中根节点之后的节点,如果该点出现在根节点的前一个位置
说明只有一颗子树,ans *= m (m叉树)
不然就遍历整个后序序列,统计子树的个数k,然后算mCk ans*= mCk
对这些子树所在的区间递归,直到只有一个节点,返回
(tm牛客网这编辑器把格式搞得跟shi一样)
*/
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<map>
using namespace std;
int ans, m;
char pre[27], post[27];
map<char, int> postidx;
//相应字母在post中的下标,因为每个节点都是唯一的,所以可以建立一个索引
void Count(int preS, int preE, int postS, int postE);
int mCk(int m, int k);

int main()
{
    while (scanf("%d %s %s", &m, pre, post) != EOF)
    {
        ans = 1;
        for (int i = 0; i<strlen(post); i++)
            postidx[post[i]] = i;
        Count(0, strlen(pre) - 1, 0, strlen(post) - 1);
        printf("%d\n", ans);
    }//while
    return 0;
}//main

void Count(int preS, int preE, int postS, int postE)//指示先序和后序某段区间
{
    if (preS >= preE)
        return;
    int i = preS + 1, cnt = 0;//cnt统计子树的个数,i是标识当前树的根节点的子树的根节点,在pre中的下标
    int idx = postidx[pre[i]];
    while (i <= preE)
    {
        Count(i, i + idx - postS, postS, idx);
        cnt++;
        if (idx != postE - 1)//子树不止一个,把要递归搜索的树的区间整体移动
        {
            i += idx - postS + 1;   //idx-postS是刚刚递归过的子树的大小
                                  //i要跨过这个区间,找到下一个要搜索的根节点
            postS = idx + 1;    //post的区间起始位置也要前进1位
            idx = postidx[pre[i]];//idx重新定位下一个要搜索的子树根节点在post中的下标
        }
        else
            break;//完成对当前区间中所有字数根节点的全部搜索
    }
    ans *= mCk(m, cnt);//计算排列组合,cnt表示当前层有几个子树
}

int mCk(int m, int k)
{
    int numerator = 1, denominator = 1;
    for (int i = 0; i<k; i++, m--)
        numerator *= m;
    for (int i = 1; i <= k; i++)
        denominator *= i;
    return numerator / denominator;
}




编辑于 2018-03-15 12:02:45 回复(3)
#include<iostream>
#include<string> 
using namespace std;  
int c[21][21],n;
long long solve(string pre,string post){  
    long long sum=1;  
    int num=0,k=0,i;  
    pre.erase(pre.begin());  
    post=post.substr(0, post.length()-1);  
    while (k<pre.length())
        for (i=0;i<post.length();i++)  
            if (pre[k]==post[i]){  
                sum*=solve(pre.substr(k,i-k+1),post.substr(k,i-k+1));  
                num++,k=i+1;break;  
            }  
    sum*=c[num][n];
    return sum;  
}  
void init(){  
    int i,j;  
    c[0][1]=c[1][1]=1;  
    for(i=2;i<21;i++)    
        for(c[0][i]=1,j=1;j<=i;j++) 
            if(i==j) c[j][i]=1;  
            else c[j][i]=c[j-1][i-1]+c[j][i-1];  
  
}  
int main(){  
    string pre,post;  
    init();  
    while(cin>>n>>pre>>post&&n)
        cout<<solve(pre,post)<<endl;
} 

发表于 2017-11-10 21:32:40 回复(0)
#include <iostream>
#include <string>
using namespace std;
// pre: root,child1->childm
// post:child1->childm,root

void getPosssible(string pre, string post, int m, int& ans) {
    if(pre == "") return;
    if (pre.front() == post.back()) {
        // 位于同一颗根节点的树上
        int len = post.size();
        ans *= m;
        getPosssible(pre.substr(1,len-1), post.substr(0,len-1), m, ans);
    } else {
        // 位于不同根节点的树上
        int rootNum = 0;
        while (!pre.empty()) {
            char root1 = pre.front();
            int root1Len = post.find(root1) ;
            getPosssible(pre.substr(1, root1Len), post.substr(0, root1Len),  m,  ans);
            rootNum++;
            int restLen = post.size() - root1Len;
            if(!restLen) break;
            pre = pre.substr(root1Len+1, restLen);
            post = post.substr(root1Len+1, restLen);
        }
        // 得到分支数
        int a = 1, b = 1;
        for (int i = 0 ; i < rootNum; i++) {
            a *= (m - i);
            b *= (rootNum - i);
        }
        ans *= (a / b);

    }
}
int main() {
    int m;
    while (cin >> m && m != 0) {
        string pre, post;
        cin >> pre >> post;
        int ans = 1;
        int len = post.size();
        getPosssible(pre.substr(1,len-1), post.substr(0,len-1), m, ans);
        cout << ans << '\n';
    }
}
// 64 位输出请用 printf("%lld")

发表于 2024-05-18 11:26:44 回复(0)
#include <iostream>
using namespace std;

//这里认为子树位置一样就算同一种,所以是用组合数而不是排列数
//注意组合数的求法:C(n,k)=C(n-1,k)+C(n-1,k-1)

const int M = 25;
int C[M][M];
int m;
string preTr, postTr;

int diffTree(string pre, string post)
{
    if(pre.size() == 1 && post.size()==1)
        return 1;
    
    int res = 1;
    pre.erase(0, 1);
    post.pop_back();

    int i=0, j=0;
    int subtree = 0;
    while(i<pre.size())
    {
        int k = post.find(pre[i], j);
        int trees = diffTree(pre.substr(i, k-j+1), post.substr(j, k-j+1));
        subtree++;
        res*=trees;
        i+=(k-j+1);
        j=k+1;
    }

    return res*C[m][subtree];
}

int main() 
{
    C[0][0]=1;
    for(int i=1;i<M;i++)
    {
        C[i][0]=1;
        for(int j=1;j<i;j++)
            C[i][j]=C[i-1][j]+C[i-1][j-1];
        C[i][i]=1;
    }
    while(cin>>m>>preTr>>postTr)
        cout<<diffTree(preTr, postTr)<<endl;

    return 0;
}
// 64 位输出请用 printf("%lld")

编辑于 2024-03-12 17:21:40 回复(0)
//又学了不少..
#include "stdio.h"
#include "string"
using namespace std;
int n;char buf1[28],buf2[28];
long long dp[21][21];

void Init(){
    dp[0][0] = 1;
    for (int i = 1; i < 21; ++i) {
        for (int j = 0; j <= i; ++j) {
            if (j == 0)
                dp[i][j] = 1;
            else if (i == j)
                dp[i][j] = 1;
            else
                dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
        }
    }
}

long long solve(string str1,string str2){//str1为前序遍历的串,str2为后序遍历的串
    str1.erase(str1.begin());
    str2.pop_back();//不考虑根节点
    int pre = 0;//pre为前序的第一个节点
    long long sum = 1;//记录可能产生的树的个数
    int num = 0;//记录有几个子树
    for (int i = 0; i < str2.size(); ++i) {
        if (str2[i] == str1[pre]){
            sum *= solve(str1.substr(pre,i-pre+1),str2.substr(pre,i-pre+1));
            pre = i + 1;
            ++num;
        }
    }
    return sum*dp[n][num];
}

int main(){
    Init();
    while (scanf("%d ",&n) != EOF){
        if (n == 0)
            break;
        else{
            scanf("%s %s",buf1,buf2);
            string str1 = buf1,str2 = buf2;
            printf("%lld\n", solve(str1,str2));
        }
    }
}
发表于 2023-03-14 21:14:04 回复(0)
#include<iostream>
#include<string>
#include<vector>
using namespace std;


/*
输入描述:
输入将包含多个问题实例。每个实例将由一行m s1 s2组成,表示树是m元树,s1是前序遍历,s2是后序遍历。所有遍历字符串将由小写字母字符组成。
对于所有输入实例,1<=m<=20,s1和s2的长度将介于1和26之间(包括1和26)。如果s1的长度是k(当然,这与s2的长度相同),则字母表的前k个字母将用于字符串。
输入行0将终止输入。
*/

/*
输出描述:
对于每个问题实例,您应该输出一行,其中包含可能的树的数量,这将导致实例的预排序和后排序遍历。所有输出值都将在32位有符号整数的范围内。
对于每个问题实例,保证至少有一个树具有给定的预排序和后排序遍历。
*/


//保存子树的前序和后序遍历结果
struct SubTree{
    
    SubTree(const string& pre,const string& post)
        :_pre(pre)
        ,_post(post)
    {}
    
    string _pre;
    string _post;
};


//求n的阶乘
long long Fac(int n)
{
    long long f = 1;
    for(int i = 1; i <= n; i++)
    {
        f *= i;
    }
    return f;
}

//求C(n,m) = C(n,n-m)
long long CalcCom(int n,int m)
{
    m = m < (n-m)?m:(n-m);
    long long r = 1;
    for(int i = n; i >= n-m+1; i--)
    {
        r *= i;
    }
    return r / Fac(m);
}


vector<SubTree> CalcSubTree(const string& pre,const string& post)
{
    size_t subRootPreIdx = 1;
    size_t postFirst = 0;  //子树在后序遍历结果中第一个元素的位置
    
    vector<SubTree> v;
    while(subRootPreIdx < pre.size())
    {
        //确定子树的根结点
        char subRoot = pre[subRootPreIdx];
        
        //确定根在后序遍历结果中的位置
        char subRootPostIdx = post.find(subRoot);
        
        //计算该子树中结点的个数
        size_t subTreeNodeCount = subRootPostIdx - postFirst + 1;
        
        //找到该棵子树钱些许遍历结果 - 找到该棵子树后序遍历结果 - 构造子树
        SubTree subTree(pre.substr(subRootPreIdx,subTreeNodeCount),post.substr(postFirst,subTreeNodeCount))
;
        v.push_back(subTree);
        
        //根据下一棵子树根在前序遍历结果中的下标
        subRootPreIdx += subTreeNodeCount;
        
        //更新下一棵子树中第一个结点在后序遍历中的下标
        postFirst += subTreeNodeCount;
    }
    return v;
}

long long CalcTreePossible(int m,const string& pre,const string& post)
{
    //如果树中只有一个结点 -- 树的可能性是唯一的
    if(1 == pre.size()){
        return 1;
    }
    
    //先找出每一层根结点的所有子树
    vector<SubTree> v = CalcSubTree(pre,post);
    
    //计算根结点子树的可能性 --- 组合
    long long result = CalcCom(m,v.size());
    
    for(auto& e: v){
        result *= CalcTreePossible(m,e._pre,e._post);  //递归
    }
    return result;
}

int main()
{
    int m;  //多少叉树
    string pre,post;
    while(cin >> m >> pre >> post)
    {
        if(m == 0){
            break;
        }
        cout << CalcTreePossible(m,pre,post) << endl;
    }
    return 0;
}

发表于 2023-03-07 19:43:46 回复(0)




import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            int m = scanner.nextInt();
            if(m==0)
                break;
            String pre = scanner.next();
            String post = scanner.next();
            ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
            System.out.println(fun(m, pre, post, 1, pre.length() - 1, 0, post.length() - 2, new ArrayList<ArrayList<Integer>>()));
            //直接跨过 整个数的根结点 一个正确的前序和后序 前序的第一个结点和后序最后一个相等
        }
    }


    private static long fun(int m, String pre, String post, int pre_start, int pre_end, int post_start, int post_end,ArrayList<ArrayList<Integer>> ret) {
        if (post_start > post_end || pre_start > pre_end)//这里说明没有左子树或者右子树 返回1(如果返回0,导致递归的乘积为0)
            return 1;
        //分离出根结点的子树 计算有多少个子树
        int count = 0;//记录子树个数
        long sum = 1;
        char pre_root = pre.charAt(pre_start);
        char post_root = post.charAt(post_end);
        while (pre_start <= pre_end) {//前序序列找 子树的根
            int i = post_start;
            if (pre_root == post_root)//只有一个子树
                i = post_end;
            //记录子树在前序和后序中的存在位置
            for (; i <= post_end; i++) {//后序序列找 子树的根
                if (pre_root == post.charAt(i)) {
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(pre_start);
                    list.add(i - post_start + pre_start);
                    list.add(post_start);
                    list.add(post_end);
                    ret.add(list);

                    count++;//找到了一个子树

                    //计算下一个子树的位置
                    pre_start = i - post_start + pre_start + 1;
                    post_start = i + 1;
                    if (!(post_start > post_end || pre_start > pre_end))
                        pre_root = pre.charAt(pre_start);
                    else
                        break;
                }
            }
        }
        //找完了这个结点的所有子树 计算可能的结果
        sum *= Fib(count, m);
        // 递归遍历子树下的可能性
        for (int i = 0; i < ret.size(); i++) {
            ArrayList<Integer> list = ret.get(i);
            sum *= fun(m, pre, post, list.get(0) + 1, list.get(1), list.get(2), list.get(3) - 1, new ArrayList<ArrayList<Integer>>());
        }
        return sum;
    }
    //求Cmn的值
    private static long Fib(int n, int m) {
        long sum = 1;
        for (int i = 0; i < n; i++) {//计算阶乘 Amn
            sum *= m;
            m--;
        }
        long div = 1;
        for (int i = 1; i <= n; i++) {   //计算n的阶乘 Ann
            div *= i;
        }
        //计算Cmn
        sum /= div;
        return sum;
    }
}

发表于 2022-09-17 00:15:56 回复(0)
先确定根节点,再确定当前根节点有多少子树,然后就可以计算这些子树有多少组合方式。
第一步完成后,还需要注意,子树也可能有子树,所以还需要按照上述步骤依次计算,并把所有组合方式乘到一起,递归处理即可。
给出全局变量,用来接收所有乘积。
#include <iostream>
#include <string>
using namespace std;
long long Factorial(int n){//求一个数的阶乘
	if (n == 0){
		return 1;
	}
	long long mul = 1;
	while (n){
		mul *= n;
		n--;
	}
	return mul;
}
long long sum = 1;//全局变量,用来保存乘积结果
void AllPossible(int n, string& str1, string& str2, int l1, int r1, int l2, int r2){
		l1 += 1;
		r2 -= 1;
		int count = 0;//统计当前树有多少子树
		int pre = 0;
		while (l1<=r1){
			pre = l2;
			while (l2<=r2&&str1[l1]!=str2[l2]){
				l2++;
			}
			AllPossible(n,str1,str2,l1,l1+l2-pre,pre,l2);
			count++;//每递归一次,子树的个数加1
			l1 = l1 + l2 - pre + 1;//计算下一次l1的起始位置
			l2++;//下一次l2的起始位置
		}
		sum *= Factorial(n)/(Factorial(count)*Factorial(n-count));//当前树所有子树的组合
}
int main(){
	int n = 11;
	string str1;
	string str2;
	while (cin >> n >> str1 >> str2){
		AllPossible(n, str1, str2, 0, str1.size() - 1, 0, str2.size() - 1);
		cout << sum << endl;
	}
	return 0;
}

编辑于 2022-06-08 20:16:22 回复(0)
import java.util.*;
class SubTree{
    public SubTree(String pre,String post){
        this.pre = pre;
        this.post = post;
    }
    String pre;
    String post;
}
public class Main{
    //计算阶乘
    public static  long fac(int n){
        long f = 1;
        for(int i = 1;i <= n;i++){
            f *= i;
        }
        return f;
    }
    //计算C n m 
    public static long calcCom(int n,int m){
         m = m < (n - m)? m : (n - m);
        long r = 1;
        for(int i = n;i >= n - m + 1;i--){
            r *= i;
        }
        return r / fac(m);
    }
    public static List<SubTree> calcSubTree(String prev,String post){
       //子树的根在前序遍历结果中的位置
        int subRootPreIdx = 1;
        //后序遍历
        int postFirst = 0;
         List<SubTree> subTreeList = new ArrayList<>();
        while(subRootPreIdx < prev.length()){
            //确认该棵子树的根节点
            char rootSub = prev.charAt(subRootPreIdx);
            int subRootPostIdx = post.indexOf(rootSub);
            int subTreeNodeCount = subRootPostIdx - postFirst + 1;
            //从前序和后续遍历结果中分离出该棵子树的前序和后序遍历结果
            SubTree subTree = new SubTree(
                  prev.substring(subRootPreIdx,subRootPreIdx + subTreeNodeCount),
                  post.substring(postFirst,postFirst + subTreeNodeCount)
            );
            subTreeList.add(subTree);
            //继续分离下一棵子树
            subRootPreIdx += subTreeNodeCount;
            postFirst += subTreeNodeCount;
        }
        return subTreeList;
    }
    public static long CalcTreePossible(int m,String pre,String post){
        if(pre.isEmpty() || pre.length() == 1){
            return 1;
        }
        //先分离出根节点有多少棵树
       List<SubTree> subTree =  calcSubTree(pre,post);
        //根节点子树可能性的组合结果
        long result = calcCom(m,subTree.size());
        //根的子树有多少种可能性
        for(SubTree e : subTree){
            result *= CalcTreePossible(m,e.pre,e.post);
        }
        return result;
    }
    public static void main(String[] args){
         Scanner in = new Scanner(System.in);
        while (in.hasNext()) {
            int m = in.nextInt();
            if(m == 0){
                break;
            }
            //接收子树的前序和后续遍历结果
            String pre = in.next();
            String post = in.next();
            
            System.out.println(CalcTreePossible(m,pre,post));
        }
    }
}

发表于 2022-05-14 19:38:11 回复(0)
#include <stdio.h>
#include <string.h>

int m;

int count(int l1,int h1,char *s1,int l2,int h2,char *s2,int n){
    if(n<=1) return 1;
    l1=l1+1;//去掉父节点
    h2=h2-1;
    n=n-1;
    int k,l=1,h=1,c=1;
    for(int i=0;i<m;i++){//拆分出子树,进行几次循环,就有几颗子树
        char key=s1[l1];
        l=l*(i+1);//子树在分支上的次序固定,分布情况是排列组合问题
        h=h*(m-i);
        for(k=l2;k<=h2;k++) if(s2[k]==key) break;
        c=c*count(l1,k-l2+l1,s1,l2,k,s2,k-l2+1);
        n=n+l2-k-1;
        l1=k-l2+l1+1;
        l2=k+1;
        if(k==h2) break;
    }
    return h*c/l;
}


int main(){
    int n,c;
    char s1[27]={'\0'};
    char s2[27]={'\0'};
    while(scanf("%d %s %s",&m,s1,s2)!=EOF){
        n=strlen(s1);
        c=count(0,n-1,s1,0,n-1,s2,n);
        printf("%d\n",c);
    }
}
发表于 2022-01-13 00:41:15 回复(0)
#include"iostream"
using namespace std;
/*计算组合数c(m, n)*/
int f(int k, int m, int n){
    if(k== n+ 1|| m== 0|| m> (n- k+ 1)){
        if(m== 0){return 1;}
        else{return 0;}
    }else{
        return f(k+ 1, m, n)+ f(k+ 1, m-1, n);
    }
}
/*以该先序和后序能形成的n叉树的个数*/
int g(string nlr, string lrn, int n){
    if(nlr.length()== 1){return 1;}// 结束条件
    else{
        int fe= 0, sum= 1, m= 0;
        while(fe< nlr.length()-1){// 考虑固定一种子树的顺序 则n叉树的个数等于各个子树所能形成n叉树个数的连乘积
            int len= lrn.find(nlr[fe+ 1])+ 1;
            string s1= nlr.substr(fe+ 1, len);
            string s2= lrn.substr(0, len);
            sum*= g(s1, s2, n);m++;fe+= len;
            lrn= lrn.substr(len);
        }
        return f(1, m, n)* sum;// 考虑全部的顺序
    }
}
int main(){
    string s1, s2;int n;
    while(cin>> n>> s1>> s2){
        cout<< g(s1, s2, n)<< endl;
    }
}

发表于 2021-05-06 15:49:09 回复(0)
#include <iostream>
#include <string>

const int N = 1000;
int n;
using namespace std;

int c[N][N]; //c[i][j] 表示从i个数里面选j个

int solve(string pre,string post)
{
    if(pre.size() == 1 && pre[0] == post[0]) return 1;
    pre.erase(pre.begin());
    post.erase(post.end() - 1);

    int sum = 1;
    int k = 0;
    int cnt = 0 ;//记录本层中有几棵树
    for(int i = 0; i < post.size(); i ++ )
    {
        if(post[i] == pre[k])
        {
            sum *= solve(pre.substr(k,i - k + 1),
                        post.substr(k,i - k + 1));
            k = i + 1;
            cnt ++;
        }
    }
    sum *= c[n][cnt];
    return sum;
}

int main()
{
    c[0][0] = 1;
    for(int i = 1; i < N; i ++)
        for(int j = 0; j <= i; j ++)
        {
            c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
        }
    string pre,post;
    while(cin >> n >> pre >> post)
    {
        cout << solve(pre,post) << endl;
    }
    return 0;
}
发表于 2021-02-24 17:55:15 回复(0)
#include<iostream>
#include<string>
using namespace std;

long long N[21],num;
int n;
void ComputeN()
{
    N[0] = 1;
    for(int i = 1;i < 21;i++)
    {
        N[i] = N[i - 1] * i;
    }
}

int CMN(int m,int n)
{
    return N[n] / (N[m] * N[n - m]); 
}

void DFS(string pre,string post)
{
    if(pre.size() == 1) return;
    int count = 0;
    pre = pre.substr(1);
    post = post.substr(0,post.size() - 1);
    while(pre != "")
    {
        int index = post.find(pre[0]) + 1;
        string newpre = pre.substr(0,index);
        string newpost = post.substr(0,index);
        pre = pre.substr(index);
        post = post.substr(index);
        count++;
        DFS(newpre,newpost);
    }
    num *= CMN(count,n);
}

int main()
{
    string s1,s2;
    ComputeN();
    while(cin >> n >> s1 >> s2)
    {
        num = 1;
        DFS(s1,s2);
        cout << num << endl;
    }
    return 0;
}

发表于 2021-02-17 13:48:15 回复(0)
找每个结点的直接子结点个数 m,这些子结点的相对顺序固定
对于 n 叉树,每个结点的可能的子结点的情况个数,是一个组合数  
所有组合数相乘,得到总的组合可能个数

用 dfs 分析出每个结点的直接子结点个数

#include <iostream>
#include <string>

int n;
long long fn;
long long total;

void CountChildren(const std::string& Pre, int s1, int e1, 
                   const std::string& Post, int s2, int e2){
    int i = s1;
    int cnt = 0;
    int back_s2 = s2;
    while(i < e1){
        for (int j = e2-1; j >= s2; j--){
            if (Pre[i] == Post[j]){
                cnt++;
                int childLen = j - s2;
                if (childLen == 0){
                    i++;
                    s2++;
                    break;
                }else{
                    if (i + 1 < Pre.size()){
                         CountChildren(Pre, i+1, i + 1 + childLen, Post, s2, j);
                    }
                    i = i + 1 + childLen;
                    s2 = s2 + 1 + childLen;
                }
            }
        }
    }
    long long poss = 1;
    for (int i = 1; i <= cnt; i ++){
        poss = poss * i;
    }
    long long poss2 = 1;
    for (int i = 1; i <= n-cnt; i++){
        poss2 = poss2 * i;
    }
    total = total * (fn / poss / poss2);
}

int main(){
    std::string N, Pre, Post;
    while(std::cin >> N){
        std::cin >> Pre >> Post;
        n = std::stoi(N);
        fn = 1;
        total = 1;
        for (int i = 1; i <= n ; i++){
            fn = fn * i;
        }
        CountChildren(Pre, 1, Pre.size(), Post, 0, Post.size()-1);
        std::cout << total << std::endl;
    }
    return 0;
}

发表于 2021-01-31 09:50:25 回复(0)
//m叉树根节点孩子结点个数为n,可以得到组合数C(m,n),递归调用根节点的各个孩子节点为根
//的子树,得到n个组合数.这n个组合数与组合数C(m,n)的和即为以先序与后序序***定的树的
//总个数
//其中每个阶段计算组合数采用分而治之的思想,类比二叉树先序后序递归调用得中序的方式
//可以将先序和后序分为n个子树对应的先序后序子序列,统计子序列的个数即为可得到n,
#include<cstdio>
(802)#include<cstring>
#include<map>
using namespace std;
char pre[30],post[30];
int m;
map<char,int> mp;
int ccal(int n1,int m1){
    int sum=1;
    for(int i=1;i<=m1;++i){
        sum=sum*(n1-m1+i)/i;
    }
    return sum;
}
int cal(int pl,int pr,int pol,int ***bsp;   if(pl>=pr){
        return 1;
    }
    int ans=1,i=pl+1,l=pol,r=mp[pre[pl+1]];
    int sum=0;
    while(i<=pr){
        ans*=cal(i,i+r-l,l,r);
        sum++;
        i=i+r-l+1;
        l=r+1;
        if(i<=pr)
           r=mp[pre[i]];
    }
    ans*=ccal(m,sum);
    return ans;
}
int main(){
    while(scanf("%d %s %s",&m,pre,post)!=EOF){
           int len=strlen(pre);
           mp.clear();
           for(int i=0;i<strlen(post);++i){
                mp[post[i]]=i;
           }
           int cnt=cal(0,len-1,0,len-1);
           printf("%d\n",cnt);
    }
    return 0;
}

发表于 2020-04-28 17:56:48 回复(0)
#include <bits/stdc++.h>
using namespace std;
int com(int n,int m)
{
    int up=1,down=1;
    for(int i=1;i<=n;up*=(m-i+1),down*=i++);
    return up/down;
}
int dfs(string pre,string post,int m)
{
    if(pre.length()==1)return 1;
    vector<string> preSplit,postSplit;
    int preCut=1,postCut1=0,postCut2;
    while(preCut!=pre.length())
    {
        postCut2=post.find(pre[preCut]);
        preSplit.push_back(pre.substr(preCut,postCut2-postCut1+1));
        postSplit.push_back(post.substr(postCut1,postCut2-postCut1+1));
        preCut+=postCut2-postCut1+1;
        postCut1=postCut2+1;
    }
    int n=preSplit.size(),sum=1;
    for(int i=0;i<n;++i)sum*=dfs(preSplit[i],postSplit[i],m);
    return sum*com(n,m);
}
int main()
{
    int  m;
    string pre,post;
    cin>>m>>pre>>post;
    cout<<dfs(pre,post,m);
    return 0;
}


发表于 2020-04-12 03:00:02 回复(0)
本质是求每个根节点有几颗子树
#include<iostream>
(720)#include<vector>
#include<string>
using namespace std;
const int N = 21;
long long sum = 1;
long long arr[N];
void Initial()
{
	arr[0] = 1;
	for (int i = 1; i < N; i++)
		arr[i] = arr[i - 1] * i;
}
long long Cnm(int n,int m)
{
	return arr[n] / (arr[m] * arr[n - m]);
}
void PrePost(int m ,string a ,string b)
{
	if (a.size() > 1)
	{
		if (a[1] == b[b.size() - 2])
		{
			sum *= Cnm(m, 1);
			a.erase(0, 1);
			b.erase(b.size() - 1);
			PrePost(m, a, b);
		}
		else
		{
			int k = 0;
			vector<string> x,y;
			int i;
			while (a.size() > 1)
			{
				for (i = 0; i < b.size(); i++)
				{
					if (b[i] == a[1])
					{
						k++;
						break;
					}
				}
				x.push_back(a.substr(1, i + 1));
				y.push_back(b.substr(0, i + 1));
				a.erase(1, i + 1);
				b.erase(0, i + 1);
			}
			sum *= Cnm(m, k);
			for (int i = 0; i < x.size(); i++)
			{
				PrePost(m, x[i], y[i]);			
			}		
		}
	
	}
}
int main()
{
	Initial();
	int n;
	string a, b;	
	while (cin>>n)
	{
		sum = 1;
		cin >> a >> b;
		PrePost(n, a, b);
		cout << sum << endl;
	}
}


发表于 2020-04-01 20:10:56 回复(0)