[线段树or笛卡尔树+简单KMP]poj4005 or hdu4125 Moles

本文探讨了如何利用二叉搜索树的特性构造特定的DFS序列,并使用KMP算法来查找目标串在DFS序列中的出现次数。通过实例演示了如何通过线段树和笛卡尔树求解L和R值,以及如何进行KMP匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:N只编号1-N的鼹鼠打洞,第i只编号为a[i],编号不重复。打的洞的样子符合以a[i]为值,以下标为插入顺序的二叉搜索树。现在从根出发,存在左子树则先走左子树,否则往右走,每经过一个洞(结点),如果这个洞的值是奇数,就记录1,否则记录0,得到的dfs序列是01串S。注意是dfs序列不是中序遍历得到的序列,每个点最多被经过3次:root->lson...lson->root->rson...rson->root.

给定一个串T,问T在S中出现多少次。

求出S之后KMP就好惹。

关键:某个点a[i]的父亲是 左边的数中 比它小的数中最大的值L 或者 比它大的数的最小值R。

L和R都不存在,a[i]是根

仅L不存在,a[i]是R的左儿子

仅R不存在,a[i]是L的右儿子

都存在则L的右儿子和R的左儿子必有一个为空,a[i]就放在空的那个地方。
可以用set求L,R,但是我写会T;  
线段树求L,R+读入挂  2938ms/3000ms  ,不加会T
笛卡尔树1719ms  

笛卡尔树看结点的key满足二叉搜索树,看结点的value满足堆(按具体实现分为小顶堆or大顶堆,此题小顶堆)
a[i]为key,i为value,则value满足早点到的一定最浅,key满足二叉搜索树。  
S,T匹配用kmp;注意这个二叉搜索树可能是一条链,dfs序列递归求可能会爆栈,用栈模拟即可√。

线段树:

 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
const int maxn=6e5+5;
int t,n,stop,a[maxn];
char S[maxn*5],T[7005];
int L[maxn<<2],R[maxn<<2];
int stk[maxn],top;
#define lson (t<<1)
#define rson (t<<1|1)
void build(int t,int l,int r){
    if(l==r){
        L[t]=-1;R[t]=1e9;return;
    }
    int mid=l+r>>1;
    build(lson,l,mid);build(rson,mid+1,r);
    L[t]=-1;R[t]=1e9;
}
void update(int t,int l,int r,int pos){
    if(l==r){
        L[t]=R[t]=l;return;
    }
    int mid=l+r>>1;
    if(pos<=mid) update(lson,l,mid,pos);
    else update(rson,mid+1,r,pos);
    L[t]=max(L[lson],L[rson]);
    R[t]=min(R[lson],R[rson]);
}
int queryL(int t,int l,int r,int pos){
    if(r<=pos) return L[t];
    int mid=l+r>>1;
    if(pos<=mid) return queryL(lson,l,mid,pos);
    else return max(L[lson],queryL(rson,mid+1,r,pos));
}
int queryR(int t,int l,int r,int pos){
    if(l>=pos) return R[t];
    int mid=l+r>>1;
    if(pos<=mid) return min(queryR(lson,l,mid,pos),R[rson]);
    else return queryR(rson,mid+1,r,pos);
}
int lchild[maxn],rchild[maxn],fa[maxn];
void dfs(int u){
    stk[top++]=u;
    while(top){
        int u=stk[top-1];
        S[stop++]=(u&1)?'1':'0';
        if(lchild[u]){
            stk[top++]=lchild[u];lchild[u]=0;
        }else if(rchild[u]){
            stk[top++]=rchild[u];rchild[u]=0;
        }else{
            top--;
        }
    }
    S[stop]='\0';
}
int nt[7000+5];
char b[10001];
//参数为模板串和next数组
//字符串均从下标0开始
//参数为模板串和next数组
//字符串均从下标0开始
void kmp_next()
{
    nt[0]=0;
    for(int i=1,j=0,m=strlen(T);i<m;i++)
    {
        while(j&&T[i]!=T[j])j=nt[j-1];
        if(T[i]==T[j])j++;
        nt[i]=j;
    }
}
int kmp()
{
    kmp_next();
    int ans=0,sn=strlen(S),tn=strlen(T);
    for(int i=0,j=0;i<sn;i++)
    {
        while(j&&S[i]!=T[j])j=nt[j-1];
        if(S[i]==T[j])j++;
        if(j==tn)
            ans++;
    }
    return ans;
}
inline char nc() {
	static char buf[100000], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}inline int _read() {
	char ch = nc(); int sum = 0;
	while (!(ch >= '0'&&ch <= '9'))ch = nc();
	while (ch >= '0'&&ch <= '9')sum = sum * 10 + ch - 48, ch = nc();
	return sum;
}
inline void  _sread(){
    char ch = nc();int p=0;
	while (!(ch >= '0'&&ch <= '9'))ch = nc();
	while (ch >= '0'&&ch <= '9')T[p++] =ch, ch = nc();
	T[p]='\0';
}
int main()
{
    t=_read();int kase=0;
    while(t--){
        n=_read();
        int root=-1;
        stop=0;
        build(1,1,n);
        for(int i=1;i<=n;i++) lchild[i]=0,rchild[i]=0;
        for(int i=1;i<=n;i++){
            a[i]=_read();
            int l=queryL(1,1,n,a[i]),r=queryR(1,1,n,a[i]);
            if(l==-1&&r==1e9) root=a[i];
            else if(l==-1){
                lchild[r]=a[i];fa[a[i]]=r;
            }else if(r==1e9){
                rchild[l]=a[i];fa[a[i]]=l;
            }else{
                if(!lchild[r]) lchild[r]=a[i],fa[a[i]]=r;
                else rchild[l]=a[i],fa[a[i]]=l;
            }
            update(1,1,n,a[i]);
        }
        _sread();
        stop=0;
        dfs(root);
     //   printf("S=%s\n",S);

        int ans=kmp();
         printf("Case #%d: %d\n",++kase,ans);
    }
    return 0;
}

笛卡尔树:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
const int N=6e5+5;
int t,n,x;

int fa[N],ls[N],rs[N];
int stk[N],top,stop;
char S[N*5],T[7005];
void dfs(int u){
    stk[top++]=u;
    while(top){
        int u=stk[top-1];
        S[stop++]=(u&1)?'1':'0';
        if(ls[u]){
            stk[top++]=ls[u];ls[u]=0;
        }else if(rs[u]){
            stk[top++]=rs[u];rs[u]=0;
        }else{
            top--;
        }
    }
    S[stop]='\0';
}
int nt[7000+5];
char b[10001];
//参数为模板串和next数组
//字符串均从下标0开始
//参数为模板串和next数组
//字符串均从下标0开始
void kmp_next()
{
    nt[0]=0;
    for(int i=1,j=0,m=strlen(T);i<m;i++)
    {
        while(j&&T[i]!=T[j])j=nt[j-1];
        if(T[i]==T[j])j++;
        nt[i]=j;
    }
}
int kmp()
{
    kmp_next();
    int ans=0,sn=strlen(S),tn=strlen(T);
    for(int i=0,j=0;i<sn;i++)
    {
        while(j&&S[i]!=T[j])j=nt[j-1];
        if(S[i]==T[j])j++;
        if(j==tn)
            ans++;
    }
    return ans;
}
int root;
struct node{
    int id,v;
    bool operator < (const node &b)const{
        return v<b.v;
    }
}e[N];
int main(){
    scanf("%d",&t);
    int kase=0;
    while(t--){
        stop=top=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&e[i].v),e[i].id=i;
        memset(ls,0,sizeof(ls));
        memset(rs,0,sizeof(rs));
        memset(fa,0,sizeof(fa));
        sort(e+1,e+n+1);
        stk[0]=0;
        for(int i = 1; i <= n; i ++){
            while(top && e[stk[top]].id > e[i].id)
            ls[i] = stk[top], top --;
            fa[i] = stk[top];
            fa[ls[i]] = i;
            if(fa[i]){
                rs[fa[i]] = i;
            }
            else{
               root=i;
            }
            stk[++ top] = i;
        }
        scanf("%s",T);
        stop=top=0;
        dfs(root);
      //  printf("%s\n",S);
        int ans=kmp();
         printf("Case #%d: %d\n",++kase,ans);
         root=0;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值