题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4125
题目意思:
给1~n组成的n个数,按输入顺序插入到平衡二叉树中,现在按1~n从小到大的顺序从根开始访问节点,每遇到一个节点i,如果i为奇数则序列+‘1’,如果i为偶数则序列+‘0’,这样得到一个序列res,给一个01序列s,求s在res中出现的次数,可以重叠。
解题思路:
首先要明白平衡二叉树的三个性质:
1、对于新插入的节点,其父亲要么是比自己大的最小的那个,或比自己小的最大的那个。
2、先序遍历为从小到大的顺序。
3、先序遍历回到根节点一共要访问2*n-1次节点。因为有n-1条边,一共有2*n-2个度,对于每个节点来说它的度数即为它的访问次数,而根节点进来的时候要加一个度,所以就是2*n-1次。
问题就转化成了,给一个数,求出在它之前的数序列中小于它的最大值或大于它的最小值。而这个问题可以用线段树维护,每个区间维护该区间已经放了数的最大值和最小值,如果查小于va的最大值,如果va>mid,则直接返回max(左区间的最大值,查右边)。同理查询大于va的最小值。
这个问题解决后,就是按先序顺序遍历,求出走的序列res.
最后KMP求出s在res中出现的次数,因为可以重复,所有每找到一个完全匹配串后,模式串下标跳到其的next值处。
代码:
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<map> #include<set> #include<stack> #include<list> #include<queue> #include<ctime> #define eps 1e-6 #define INF 0x3f3f3f3f #define PI acos(-1.0) #define ll __int64 #define lson l,m,(rt<<1) #define rson m+1,r,(rt<<1)|1 #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; #define Maxn 610000 int n; struct Inte { int mi,ma; //线段树维护区间的最大值和最小值 }inte[Maxn*4]; struct Node //静态存储,左右孩子节点 { int l,r; }node[Maxn]; void pushup(int v) //向上更新 { inte[v].mi=min(inte[v<<1].mi,inte[v<<1|1].mi); inte[v].ma=max(inte[v<<1].ma,inte[v<<1|1].ma); } void build(int l,int r,int rt) { if(l==r) //建树 { inte[rt].mi=INF; //把最小值置为无穷大,表示不存在 inte[rt].ma=0; //把最大值置为0 return ; } int m=(l+r)>>1; build(lson); build(rson); pushup(rt); } void update(int l,int r,int rt,int va) //单点更新 { if(l==r) { inte[rt].mi=inte[rt].ma=va; return ; } int m=(l+r)>>1; if(va<=m) update(lson,va); else update(rson,va); pushup(rt); } int query1(int l,int r,int rt,int va) //查询小于va的最大值 { if(l==r) return inte[rt].ma; int m=(l+r)>>1; if(va<=m) //不需要查询右区间了 return query1(lson,va); else //左区间的直接返回后比较 return max(inte[rt<<1].ma,query1(rson,va)); } int query2(int l,int r,int rt,int va)//查询大于va的最小值 { if(l==r) return inte[rt].mi; int m=(l+r)>>1; if(va<=m) return min(inte[rt<<1|1].mi,query2(lson,va)); else return query2(rson,va); } char res[Maxn<<1]; int s,cnt; char bb[7500]; void dfs(int cur) //按先序遍历搜 { res[++cnt]=(cur&1)?'1':'0'; //走到改点 if(node[cur].l) //如果有左儿子 { dfs(node[cur].l); if(node[cur].r) //如果有右儿子 { res[++cnt]=(cur&1)?'1':'0';//访问完左子树,回到它自己 dfs(node[cur].r); //再访问右子树 } res[++cnt]=(cur&1)?'1':'0';//回到自己 return ; } if(node[cur].r) //只有右子树 { dfs(node[cur].r); res[++cnt]=(cur&1)?'1':'0'; //访问完右子树后回到自己 } } int next[7500],nn; void getnext() //模式串求next数组 { int j=0; next[1]=0; for(int i=2;i<=nn;i++) { while(j>0&&bb[j+1]-bb[i]) j=next[j]; if(bb[j+1]==bb[i]) j++; next[i]=j; } return ; } int KMP() // { int j=0; int ans=0; for(int i=1;i<=cnt;i++) { while(j>0&&bb[j+1]-res[i]) j=next[j]; if(bb[j+1]==res[i]) j++; if(j==nn) { ans++; j=next[j]; } } return ans; } int main() { int t; scanf("%d",&t); for(int ca=1;ca<=t;ca++) { scanf("%d",&n); build(1,n,1); //建树 scanf("%d",&s); update(1,n,1,s);//插到叶子 cnt=0; memset(node,0,sizeof(node)); for(int i=2;i<=n;i++) { int a,al,ar; scanf("%d",&a); al=query1(1,n,1,a); //小于a的最大值 ar=query2(1,n,1,a); //大于a的最小值 if(!al) //不存在小于a的最大值 node[ar].l=a; //那么只能是以大于a的最小值作为父亲 else if(ar==INF) //不存在自己作为左儿子的父亲 node[al].r=a; //那么只可能是自己是右儿子的父亲 else { if(node[al].r) //已经有了 node[ar].l=a; else node[al].r=a; } update(1,n,1,a);//加到线段树中 } /* for(int i=1;i<=n;i++) printf("i:%d l:%d r:%d\n",i,node[i].l,node[i].r);*/ cnt=0; dfs(s); scanf("%s",bb+1); nn=strlen(bb+1); getnext(); int ans=KMP(); printf("Case #%d: %d\n",ca,ans); } return 0; }