SUDT 3926 bLue的二叉树 [KMP or hash]【思维】

题目链接:http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Contest/contestproblem/cid/2176/pid/3926.html
——————————————————————————————————————
bLue的二叉树
Time Limit: 3000MS Memory Limit: 65536KB
Submit Statistic
Problem Description

Keke 是一个喜爱种树的人,他对各种树都有很深的研究。
MLE 听说 bLue 种了一些新品种的树,就想邀请 Keke 去围观一下。
PBH 在暗中把这一切尽收眼底,作为资深植树行家,他虽不屑,但也决定和他们一起去看一看。
于是,大家便一起到了 bLue 家去看树。
bLue 有两棵二叉树,分别有 n 和 m 个节点,编号分别为 1-n 和 1-m,每个节点都有一个权值,bLue 想知道第一棵树的所有子树中与第二棵树完全相同的个数(不考虑节点编号)。
Input

输入数据有多组,组数不超过 150,到 EOF 结束。
每组第一行有两个整数 n (0 < n <= 1e5)和 m (0 < m <= 1e5),表示第一棵树和第二棵树的节点个数;
接下来 n 行,表示第一棵树:第 i (0 < i <= n) 行有 3 个整数,w[i] (0 < w[i] <= 10), lc[i], rc[i] (0 < lc[i], rc[i] <= n),分别表示节点 i 的权值,该节点的左孩子编号和右孩子编号,若某个孩子不存在,则为 0 (数据保证每棵树都是合法的有根二叉树);
接下来 m 行,表示第二棵树:格式同第一棵树;
保证:树的最大深度不会超过 10000。数据量比较大,请用 scanf 读入!
Output

对于每组数据,输出一行一个整数 num,表示第一棵树的所有子树中与第二棵树完全相同的个数。
Example Input

7 4
1 6 3
2 0 4
1 7 0
3 0 0
1 2 1
2 0 0
2 0 0
2 0 0
1 4 0
1 1 2
2 0 0
3 3
1 0 0
2 1 3
3 0 0
1 0 3
2 1 0
3 0 0
Example Output

1
0
Hint

Author

不得不放弃、
——————————————————————————————————————
首先确定的是严格确定 二叉树的左子树和左子树对 上右子树和右子树对上才行

对于子树问题考虑dfs序
对于一个dfs序,区间内的东西都是子树的,那么对于两个完全一样的子树.遍历出来的区间也是一模一样的.

但是为了避免有多个子树遍出结果一样的,我们只需把空节点当作一个不一样的权值加入到树中,这样既能确保区间序列一样,同时又不会冲突.

最后对两个dfs序KMP即能求解


其实这题是能够hash做的 ,最开始我也在想hash的方法,但是想不到怎么处理左右儿子的左/右儿子到底在那,所以不太行,,== 现在想到对dfs序后他的位置l的x^l的系数 感觉很可行啊 ,同时也要做dfs==! 其实还是字符串hash了,,,,,

附本题两份KMP+hash代码
——————————————————————————————————————

#include <bits/stdc++.h>
typedef long long int LL;
using namespace std;

#define abs(x) (((x)>0)?(x):-(x))

const int N    = 1e5+10;
const int MOD  = 1e9+7;

int sum;
LL read(){
    LL x=0;char ch=getchar();
    while('0'>ch||ch>'9') ch=getchar();
    while('0'<=ch&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';sum++;ch=getchar();}
    return x;
}
/******************************************/

int n,m;
int a[N][3],b[N][3],c[N<<1],d[N<<1],lc,ld;

void dfs1(int rt){
    c[lc++]=a[rt][0];//printf("%d ",a[rt][0]);
    if(rt==0) return;
    dfs1(a[rt][1]);
    dfs1(a[rt][2]);
}

void dfs2(int rt){
    d[ld++]=b[rt][0];//printf("%d ",b[rt][0]);
    if(rt==0) return;
    dfs2(b[rt][1]);
    dfs2(b[rt][2]);
}
int Next[N<<1];//Next[i] 表示从[0~i]中最长公共前缀的长.
void get_next(int *s,int len){
    for(int i=0,j=-1;i<=len;++i,++j){
        Next[i]=j;
        while(j>=0&&s[i]!=s[j]) j = Next[j];
    }
//    for(int i=0;i<=len;i++) printf("%d%c",Next[i],(i==len)?'\n':' ');
}
//在串s上找sz
int KMP (int *s,int len,int *sz,int l){
    int i=0,j=0,cnt=0;
    while(i<len/*&&j<l*/){
        if(s[i]==sz[j])  i++,j++;
        else{
            if(0==j)  i++;
            else      j=Next[j];
        }
        if(j==l) cnt++;
    }
//   return (j==l)?(i-l+1):-1; //找第一次出现的位置
    return cnt; //找出现的个数
}
bool visa[N],visb[N];
int main(){
    while(~scanf("%d%d",&n,&m)){
        lc=ld=0;
        for(int i=1;i<=n;i++)  a[i][0]=read(),a[i][1]=read(),a[i][2]=read(),visa[a[i][1]]=visa[a[i][2]]=1;
        for(int i=1;i<=m;i++)  b[i][0]=read(),b[i][1]=read(),b[i][2]=read(),visb[b[i][1]]=visb[b[i][2]]=1;
        for(int i=1;i<=n;i++)  if(visa[i]==0){dfs1(i);break;}//puts("");
        for(int i=1;i<=m;i++)  if(visb[i]==0){dfs2(i);break;}//puts("");

        get_next(d,ld);
        printf("%d\n",KMP(c,lc,d,ld));
        for(int i=1;i<=n;i++) visa[i]=0;
        for(int i=1;i<=m;i++) visb[i]=0;
    }
    return 0;
}


/***************************************************
User name: tabris
Result: Accepted
Take time: 872ms
Take Memory: 596KB
Submit time: 2017-06-05 00:39:32
****************************************************/

#include <bits/stdc++.h>
typedef unsigned long long int LL;
using namespace std;

#define abs(x) (((x)>0)?(x):-(x))

const int N    = 1e5+10;
const int MOD  = 1e9+7;

int sum;
LL read(){
    LL x=0;char ch=getchar();
    while('0'>ch||ch>'9') ch=getchar();
    while('0'<=ch&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';sum++;ch=getchar();}
    return x;
}
/******************************************/

int n,m,ans,lc,ld;
int a[N][3],b[N][3];//,lb[N],rb[N];
LL c[N<<1],x,h,tota,totb;

void dfs1(int rt){
    ++lc;
    c[lc]=a[rt][0]*x;x*=h;
    if(rt==0) return;
    dfs1(a[rt][1]);
    dfs1(a[rt][2]);
}

void dfs2(int rt){
    ++ld;
    totb+=b[rt][0]*x;x*=h;
    if(rt==0) return;
    dfs2(b[rt][1]);
    dfs2(b[rt][2]);
}

bool visa[N],visb[N];
int main(){
    a[0][0]=b[0][0]=101;h=27;c[0]=0;
    while(~scanf("%d%d",&n,&m)){
        lc=ld=0;ans=0;tota=totb=0;
        for(int i=1;i<=n;i++)  a[i][0]=read(),a[i][1]=read(),a[i][2]=read(),visa[a[i][1]]=visa[a[i][2]]=1;
        for(int i=1;i<=m;i++)  b[i][0]=read(),b[i][1]=read(),b[i][2]=read(),visb[b[i][1]]=visb[b[i][2]]=1;
        x=1;for(int i=1;i<=n;i++)  if(visa[i]==0){dfs1(i);break;}//puts("");
        x=1;for(int i=1;i<=m;i++)  if(visb[i]==0){dfs2(i);break;}//puts("");

        for(int i=1;i<ld;i++) tota+=c[i];
        for(int i=ld;i<=lc;i++){
            tota-=c[i-ld],tota+=c[i];
            if(tota==totb) ans++;
            totb*=h;
        }

        printf("%d\n",ans);
        for(int i=1;i<=n;i++) visa[i]=0;
        for(int i=1;i<=m;i++) visb[i]=0;
    }
    return 0;
}




/***************************************************
User name: tabris
Result: Accepted
Take time: 884ms
Take Memory: 2244KB
Submit time: 2017-06-05 01:34:54
****************************************************/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值