SP3109 STRLCP - Longest Common Prefix 题解

SP3109 STRLCP - Longest Common Prefix 题解

某省某年省选原题出处,看来 CCF 出原题这事由来已久。

简化题意

  1. 让你维护一个字符串序列。
  2. 支持单点修改。
  3. 支持单点插入。
  4. 支持询问两个子串的最长公共前缀。

解法

本篇题解前置芝士:无旋 Treap(FHQ Treap),哈希,二分。如果有不会的请自行查找资料学习。

如果没有单点插入操作,我们可以考虑使用线段树维护区间哈希值(我不会后缀自动机)。求最长公共前缀时可以二分最长公共前缀的长度,这里记作 m i d mid mid,判断两个字符串前 m i d mid mid 个字符是否相同(即哈希值相等),如果相同则证明答案不小于 m i d mid mid,将二分范围向右侧缩小,如果不相同则证明答案小于 m i d mid mid,将二分范围向左缩小。

为什么这题不能用普通线段树呢?因为单点插入操作会改变序列长度,就会改变线段树中父亲与儿子的关系。

那有没有能同时支持动态维护序列和区间信息的数据结构呢?有,平衡树可以动态维护序列。相信大家都会平衡树的基本操作了,这里只有合并区间信息这里和模板题不一样。所以我只讲如何合并区间信息。

先看线段树是如何合并区间哈希值的。

线段树

注意到平衡树的性质,根节点表示的点在左右子树表示的区间的中间。所以类似于线段树的区间合并,平衡树的区间合并是三个区间的合并。

平衡树

查询 [ l , r ] [l,r] [l,r] 区间的哈希值时可以将平衡树分裂成 [ 1 , l − 1 ] , [ l , r ] , [ r + 1 , L ] [1,l-1],[l,r],[r+1,L] [1,l1],[l,r],[r+1,L] 三棵平衡树,查询后再将三棵平衡树合并即可。 L L L 表示当前序列的长度。

综上,平衡树的时间复杂度是 O ( ( L + c ) log ⁡ L + q log ⁡ 2 L ) \mathcal{O}((L + c) \log L + q \log^2 L) O((L+c)logL+qlog2L),分别是建树、修改、查询的时间复杂度,其中 q q q 表示操作中查询次数, c c c 表示操作中修改次数。

代码

#include<bits/stdc++.h>
namespace fast_IO
{
	/**
	 * 快读快写
	*/
};
using namespace fast_IO;
const int base=131;
int t,n,m,l,r,mid,ans;
char op,ch;
unsigned int pw[100010];
std::string st;
struct fhq_node
{
	int w,siz;
	unsigned int val,has;
	fhq_node *lc,*rc;
	inline fhq_node(unsigned int val)
	{
		this->val=has=val,w=rand(),siz=1,lc=rc=nullptr;
	}
	inline void pushup()
	{
		siz=(lc==nullptr?0:lc->siz)+(rc==nullptr?0:rc->siz)+1;
		if(lc==nullptr && rc==nullptr) has=val;
		else if(lc==nullptr) has=val*pw[rc->siz]+rc->has;
		else if(rc==nullptr) has=lc->has*pw[1]+val;
		else has=lc->has*pw[rc->siz+1]+val*pw[rc->siz]+rc->has;
	}
};
class fhq_treap
{
private:
	fhq_node *root;
	inline fhq_node *merge(fhq_node *l,fhq_node *r)
	{
		if(l==nullptr) return r;
		if(r==nullptr) return l;
		if(l->w<r->w)
		{
			l->rc=merge(l->rc,r),l->pushup();
			return l;
		}else
		{
			r->lc=merge(l,r->lc),r->pushup();
			return r;
		}
	}
	inline std::pair<fhq_node *,fhq_node *> split(fhq_node *rt,const int &k)
	{
		std::pair<fhq_node *,fhq_node *> ret;
		if(rt==nullptr) return std::make_pair(nullptr,nullptr);
		int left=rt->lc==nullptr?0:rt->lc->siz;
		if(k<=left) ret=split(rt->lc,k),rt->lc=ret.second,rt->pushup(),ret.second=rt;
		else ret=split(rt->rc,k-left-1),rt->rc=ret.first,rt->pushup(),ret.first=rt;
		return ret;
	}
	inline void clear(fhq_node *rt)
	{
		if(rt==nullptr) return;
		if(rt->lc!=nullptr) clear(rt->lc);
		if(rt->rc!=nullptr) clear(rt->rc);
		delete rt;
	}
public:
	inline int getsiz()
	{
		return root->siz;
	}
	inline void clear()
	{
		clear(root),root=nullptr;
	}
	inline void fix(const int &pos,const unsigned int val)
	{
		std::pair<fhq_node *,fhq_node *> a,b;
		a=split(root,pos),b=split(a.first,pos-1);
		b.second->val=b.second->has=val,root=merge(merge(b.first,b.second),a.second);
	}
	inline void insert(const int &pos,const unsigned int val)
	{
		fhq_node *rt=new fhq_node(val);
		std::pair<fhq_node *,fhq_node *> a;
		a=split(root,pos),root=merge(merge(a.first,rt),a.second);
	}
	inline unsigned int ask(const int &L,const int &R)
	{
		std::pair<fhq_node *,fhq_node *> a,b;
		a=split(root,R),b=split(a.first,L-1);
		unsigned int ret=b.second->has;
		root=merge(merge(b.first,b.second),a.second);
		return ret;
	}
};
fhq_treap tree;
int main()
{
	srand(time(0)),pw[0]=1;
	for(int i=1;i<=100001;i++) pw[i]=pw[i-1]*base;
	in>>t;
	while(t--)
	{
		in>>st>>m,st="#"+st,n=st.size()-1,tree.clear();
		for(int i=1;i<=n;i++) tree.insert(i,st[i]-'a'+1);
		for(int i=1,x,y;i<=m;i++)
		{
			in>>op>>x;
			if(op=='Q')
			{
				in>>y;
				if(x>y) std::swap(x,y);
				l=1,r=tree.getsiz()-y+1,ans=0;
				while(l<=r)
				{
					mid=(l+r)/2;
					if(tree.ask(x,x+mid-1)==tree.ask(y,y+mid-1)) ans=mid,l=mid+1;
					else r=mid-1;
				}
				out<<ans<<'\n';
			}else if(op=='R') in>>ch,tree.fix(x,ch-'a'+1);
			else in>>ch,tree.insert(x,ch-'a'+1);
		}
	}
	fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/200145.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法训练营】算法分析实验(递归实现斐波那契+插入排序、分治思想实现归并排序+快排)附代码+解析

![0 &#x1f308;欢迎来到算法专栏 &#x1f64b;&#x1f3fe;‍♀️作者介绍&#xff1a;前PLA队员 目前是一名普通本科大三的软件工程专业学生 &#x1f30f;IP坐标&#xff1a;湖北武汉 &#x1f349; 目前技术栈&#xff1a;C/C、Linux系统编程、计算机网络、数据结构、M…

c语言-数据在内存中的存储

文章目录 1. 整数在内存中的存储2. 大小端字节序和字节序判断3. 浮点数在内存中的存储 1. 整数在内存中的存储 1.整数的2进制表示方法有三种&#xff0c;即 原码、反码和补码 2. 三种表示方法均有符号位和数值位两部分&#xff0c;符号位都是用0表示“正”&#xff0c;用1表示“…

Python入门05 print函数

目录 1 Python中的内置函数2 print函数介绍3 print函数的用途总结 1 Python中的内置函数 Python中内置了很多函数&#xff0c;我们可以直接调用&#xff0c;以下是一些常见的函数&#xff1a; abs()&#xff1a;返回一个数的绝对值。all()&#xff1a;判断一个可迭代对象中的…

zookeeper实操课程Acl 访问权限控制,命令行测试

本系列是zookeeper相关的实操课程&#xff0c;课程测试环环相扣&#xff0c;请按照顺序阅读测试来学习zookeeper。阅读本文之前&#xff0c;请先阅读----​​​​​​zookeeper 单机伪集群搭建简单记录&#xff08;实操课程系列&#xff09;。 阅读本文之前&#xff0c;请先阅读…

Linux脚本sed命令

目录 一. sed命令定义 二. sed命令选项 三. sed语法选项 四. 案例解释 1. 打印奇数或偶数行 2. 打印固定行数 3. 打印包含字符的行 4. 打印特定字符首尾行 5. 删除固定行数 6. 删除特定字符行 7. 插入在固定行中 8. 替换规定行数 9. 使用变量 10. 多点编辑 11. 分…

C++ 红黑树的封装

一.map/set的封装 在实现了红黑树的部分功能后&#xff0c;我们可以便可以将红黑树作为底层结构来封装map 和 set &#xff0c;但是问题也随之而来。我们都知道map是k-v的数据模型&#xff0c;而set是k的数据模型&#xff0c;我们难道要去使用两棵红黑树来封装吗&#xff1f;显…

c++day1

提示并输入一个字符串&#xff0c;统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数 要求使用C风格字符串完成 #include <iostream>using namespace std;int main() {string str;cout << "请输入一个含有大小写字母&#xff0c;空格&am…

智慧工地信息化管理系统源码带APP

需求痛点&#xff1a;建筑行业是一个安全事故多发的行业。目前&#xff0c;工程建设规模不断扩大&#xff0c;工艺流程纷繁复杂&#xff0c;如何搞好现场施工现场管理&#xff0c;控制事故发生频率&#xff0c;一直是施工企业、政府管理部门关注的焦点。利用现代科技&#xff0…

YOLO改进系列之SKNet注意力机制

摘要 视皮层神经元的感受野大小受刺激的调节即对于不同的刺激&#xff0c;卷积核的大小应该不同&#xff0c;但在构建CNN时一般在同一层只采用一种卷积核&#xff0c;很少考虑因采用不同卷积核。于是SKNet被提出&#xff0c;在SKNet中&#xff0c;不同大小的感受视野&#xff…

抖音本地生活服务商申请要多久审核通过?

近年来&#xff0c;随着互联网的普及和社交媒体的兴起&#xff0c;本地生活服务行业也迎来了巨大的发展机遇。作为最受欢迎的短视频平台之一&#xff0c;抖音也不例外。抖音本地生活服务商申请要多久审核通过&#xff1f;这是许多想要加入抖音本地服务行业的人们最关心的问题之…

XUbuntu22.04之隐藏顶部任务栏(一百九十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【产品设计】SaaS产品数据分析之指标与标签

数据分析能够应用到各个领域和岗位&#xff0c;那么在SaaS产品中的应用会是如何&#xff1f;本文将探索SaaS产品在数据分析中的应用&#xff0c;并对其指标与标签的设计进行总结分析&#xff0c;一起来看看吧。 数据分析是业务开展过程中&#xff0c;收集记录各种行为产生的数据…

大数据平台/大数据技术与原理-实验报告--部署全分布模式HBase集群和实战HBase

实验名称 部署全分布模式HBase集群和实战HBase 实验性质 &#xff08;必修、选修&#xff09; 必修 实验类型&#xff08;验证、设计、创新、综合&#xff09; 综合 实验课时 2 实验日期 2023.11.07-2023.11.10 实验仪器设备以及实验软硬件要求 专业实验室&#xff…

Maven——仓库

Maven坐标和依赖是任何一个构件在Maven世界中的逻辑表示方式&#xff1b;而构件的物理表示方式是文件&#xff0c;Maven通过仓库来统一管理这些文件。 1、何为Maven仓库 在Maven世界中&#xff0c;任何一个依赖、插件或者项目构建的输出&#xff0c;都可以称为构件。例如&…

00.本地搭建 threejs 文档网站(网页版是外网比较慢)

three官网 https://threejs.org/ 下载代码 进入官网 可以选择github去下载 或者 下载压缩包 github 下载https链接地址 https://github.com/mrdoob/three.js.git git clone https://github.com/mrdoob/three.js.git安装依赖启动程序 安装依赖 npm i 或者 pnpm i 或者 …

TOPK问题的求解

在这片文章详解二叉树-CSDN博客中我们提到&#xff0c;如果要在非常多的数据(内存存不下)中找到最大或最小的前K个数&#xff0c;我们需要先构建一个K个数的小堆或大堆&#xff1b;再跟堆顶数据比较 要找最大的前K个数建小堆&#xff1b;要找最小的前K个数建大堆 1.构造数据 既…

怎样去除视频上的水印?这几个视频去水印方法简单无痕

作为全民自媒体时代&#xff0c;越来越多的人投身于自媒体行业&#xff0c;对于初学者&#xff0c;往往会遇到网上下载的视频素材会嗲有水印&#xff0c;影响二次创作以及视频观看度&#xff0c;那么怎样去除视频上的水印呢&#xff1f;别着急&#xff0c;今天分享三种视频去水…

数组元素积的符号

数组元素积的符号 描述 : 已知函数 signFunc(x) 将会根据 x 的正负返回特定值&#xff1a; 如果 x 是正数&#xff0c;返回 1 。如果 x 是负数&#xff0c;返回 -1 。如果 x 是等于 0 &#xff0c;返回 0 。 给你一个整数数组 nums 。令 product 为数组 nums 中所有元素值的…

Android 实现环形进度条

一、项目需求 项目中常常需要用到进度条&#xff0c;很简单&#xff0c;这儿做一个简单的总结和实现 二、实现控件 ProgressBar 三、实现代码 1、水平的进度条 xml布局代码&#xff1a; <ProgressBarandroid:id"id/rocketProgressBar"style"style/Wid…

【坤坤之夜 KUNKUNNIGHT】- 探索神秘世界,开启刺激冒险之旅!

你是否准备好迎接一个充满挑战和惊喜的单机游戏体验&#xff1f;坤坤之夜&#xff08;KUNKUNNIGHT&#xff09;将带你进入一个神秘而刺激的世界&#xff0c;让你尽情探索&#xff0c;解锁各种有趣的技能和道具&#xff0c;解决谜题&#xff0c;完成各种挑战。 坤坤之夜的游戏画…