当前位置: 首页 > article >正文

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;
}

http://www.kler.cn/a/149269.html

相关文章:

  • 深入探索:Scrapy深度爬取策略与实践
  • 【力扣热题100】[Java版] 刷题笔记-169. 多数元素
  • 2023年MathorCup数学建模B题城市轨道交通列车时刻表优化问题解题全过程文档加程序
  • Redo与Undo的区别:数据库事务的恢复与撤销机制
  • Flink1.19编译并Standalone模式本地运行
  • Javaweb—Ajax与jQuery请求
  • 0基础学习VR全景平台篇第123篇:VR视频航拍补天 - PR软件教程
  • 前端---CSS篇(详解CSS)
  • 微服务--03--OpenFeign 实现远程调用 (负载均衡组件SpringCloudLoadBalancer)
  • 【 Kubernetes 风云录 】- Istio 应用多版本流量控制
  • 【古月居《ros入门21讲》学习笔记】17_launch启动文件的使用方法
  • dockerfile指令学习
  • ubuntu改window任务栏
  • Leetcode—2336.无限集中的最小数字【中等】
  • 随笔(持续更新)
  • SELinux零知识学习三十七、SELinux策略语言之约束(1)
  • 2023年合肥市瑶海区某校校赛真题(小学组)
  • 图书管理系统源码,图书管理系统开发,图书借阅系统源码整体功能演示
  • MySQL(主从复制)
  • C语言数据结构之顺序表(上)
  • VT-VSPA1-1X比例压力阀控制板
  • Roll-A-Ball 游戏
  • fastadmin学习笔记-----动态下拉框
  • PWM 正玄波形 通过C语言生成
  • 宕机对独立服务器会有啥影响?
  • 音视频5、libavformat-1