ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【无旋treap hash】匹配(2022.5.21)

2022-05-24 16:02:54  阅读:184  来源: 互联网

标签:hash 21 .. int 无旋 len child 节点


上题目!

题目

1.1 题目描述

给定一个仅含小写字母的字符串 S[0..n-1],对于一个询问 (p, q, len),我们想知道它的两个子串 S[p..p+len-1]、S[q..q+len-1] 是否相同。更多地,我们希望在对串 S 完成一些操作之后还能高效地得到这个结果。 我们具体要维护以下几个操作(其中 L 为操作之前的串长):
● 1 p c:在下标 p 之前插入一个小写字母 c,0<=p<=L,p=0 意味在头插入,p=L意味在尾插入;
● 2 p: 删除 S[p],其中 0<=p<L;
● 3 p q: 将 S[p..q] 翻转为 S[q..p],例如 batta 经过 “3 1 3” 之后为 bttaa;
● 4 p q len: 表示一次询问操作,询问 S[p..p+len-1] 与 S[q..q+len-1] 是否相同,其中 0<=p<=p+len-1<L,0<=q<=q+len-1<L。

1.2 输入格式

第 1 行为两个整数 n, m,分别表示字符串的初始长度和操作次数;
第 2 行为一个长度为 n 的字符串;
接下来 m 行,每行为一个操作。

1.3 输出格式

仅一行一个整数,表示询问相同的次数。

1.4 样例输入

4 4                   
aacb                 
2 2                    // aab 
1 2 b                // aabb 
3 1 2                // abab 
4 0 2 2            // ab == ab 

1.5 样例输出

1.6 数据范围与约定

对于前20%的数据满足n,m≤1000; 
对于前70%的数据满足仅含有3、4 两种操作; 
对于100%的数据满足1≤n, m≤200000。`

第一眼看就知道是打treap或者splay的模板题由于本人过菜根本不想打又臭又长的旋转树

于是!就有了这么一个FHQ无旋treap树!(FHQ大佬%%%)

FHQ无旋treap

其树的核心在于split(分裂)和merge(合并)两个操作

先来看以val(树的大小)划分的分裂:

inline void split(int nown,int &l,int &r,int val){
if(!nown){
	l=r=0;//分到底
	return ;
}
if(size[ls[nown]]+1<=val)l=nown,split(rs[nown],rs[nown],r,val-size[ls[nown]]-1);//当前节点的size小于等于分裂值,分到左边
else r=nown,split(ls[nown],l,ls[nown],val);//同上,分到右边
update(nown);
return ;
}

注意这么一个回传值的写法

再看合并:

inline void merge(int &k,int l,int r){
if(l==0||r==0){
	k=l+r;//到底,选择其中不为0的节点回传
	return ;
}
if(num[l]>num[r])k=l,merge(rs[l],rs[l],r);//num即满足节点大根堆性质的rand()值
else k=r,merge(ls[r],l,ls[r]);
update(k); 
return ;
}

在某一个位置加入或删除节点即按照其节点位置前后分裂,加入或删除这个节点后合并

其他的操作都和普通treap大同小异

关于HASH

此题需要我们同时维护两个hash值,因为需要交换当前节点的子节点,所以要维护当前节点未交换和交换了的hash值,即:

维护以该结点为根的子树的中序字符串的哈希值(记为hash[u][0]),以及该字符串翻转后的哈希值(记为hash[u][1]),
则有:hash[u][0] = hash[child[u][0]][0]*pow[size[child[u][1]]+1]+
character[u]*pow[size[child[u][1]]]+hash[child[u][1]][0],
其中child[u][0/1]表示u的左/右儿子,size[u]表示以u为根的子树大小,pow[i]表示哈希的进制BASE的i次方。
hash[u][1]的计算同理,有:hash[u][1] = hash[child[u][0]][1]
+character[u]*pow[size[child[u][0]]]+hash[child[u][1]][1]*pow[size[child[u][0]]+1]。

交换一个节点的左右儿子时,不仅要交换它的左右儿子,也要交换它的hash[u][0]和hash[u][1]。

一些小细节

关于写tag标记是否旋转tag最好记录当前节点的子节点是否需要旋转否则计算hash会出错这件事

标签:hash,21,..,int,无旋,len,child,节点
来源: https://www.cnblogs.com/T-water/p/16305841.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有