题解:SP3109 STRLCP - Longest Common Prefix

三倍经验:

UVA11996 Jewel Magic

P4036 [JSOI2008] 火星人

题意

维护一个字符串 S,支持以下操作:

  • Q i j:输出 LCP(S[il],S[jl])

  • R i char:用 char 替换 S 的第 i 个字符

  • I i char: 在 S 的第 i 个字符后插入 char

分析

看到 LCP 相关的操作,想到后缀全家桶。

但是发现并不是很好操作。

考虑使用万能的哈希。

我们可以二分长度 x,然后用哈希检验区间 [i,i+x1] 是否等于区间 [j,j+x1]

因为有插入操作,所以使用平衡树维护哈希。

单次查询时间复杂度 O(log2n)

Code

#include<bits/stdc++.h>
using namespace std;
#define lx 323
typedef uint64_t hash_t;
#define maxn 400005
hash_t lev[maxn];
struct Treap
{
#define siz(x) (x?x->siz:0)
#define hsh(x) (x?x->hsh:0)
#define rhsh(x) (x?x->rhsh:0)
mt19937 rnd;
Treap(uint32_t s=114) { rnd.seed(s); }
struct node
{
node *lc, *rc;
uint32_t siz, id;
uint8_t ch;
hash_t hsh, rhsh;
node(uint8_t c, uint32_t i)
{
id=i;
lc=rc=nullptr;
siz=1, hsh=rhsh=ch=c;
}
node *push_up()
{
siz=siz(lc)+siz(rc)+1;
hsh=lev[siz(rc)+1]*hsh(lc)+lev[siz(rc)]*ch+hsh(rc);
rhsh=lev[siz(lc)+1]*rhsh(rc)+lev[siz(lc)]*ch+rhsh(lc);
return this;
}
};
node *rt;
node *new_node(uint8_t c) { return new node(c, rnd()); }
void split(node *x, uint32_t k, node *&l, node *&r)
{
if(!x) return l=r=0, void();
if(siz(x->lc)<k) l=x, split(x->rc, k-siz(x->lc)-1, x->rc, r);
else r=x, split(x->lc, k, l, x->lc);
x->push_up();
}
node *merge(node *x, node *y)
{
if(!x||!y) return x?x:y;
if(x->id<y->id)
{
x->rc=merge(x->rc, y);
return x->push_up();
}
else
{
y->lc=merge(x, y->lc);
return y->push_up();
}
}
void push_back(uint8_t c) { rt=merge(rt, new_node(c)); }
void insert(int p, uint8_t c)
{
node *a, *b;
split(rt, p, a, b);
rt=merge(a, merge(new_node(c), b));
}
void modify(int p, uint8_t ch)
{
node *a, *b, *c;
split(rt, p-1, a, b);
split(b, 1, b, c);
b->hsh=b->rhsh=b->ch=ch;
rt=merge(merge(a, b), c);
}
hash_t query(int l, int r)
{
node *a, *b, *c;
split(rt, l-1, a, b);
split(b, r-l+1, b, c);
hash_t ret=b->hsh;
rt=merge(merge(a, b), c);
return ret;
}
}tr(114514);
string s;
bool chk(int p1, int p2, int l, int mxlen)
{
if(p1+l-1>mxlen||p2+l-1>mxlen) return 0;
hash_t h1=tr.query(p1, p1+l-1);
hash_t h2=tr.query(p2, p2+l-1);
return h1==h2;
}
int LCP(int p1, int p2)
{
if(!tr.rt) return 0;
int len=tr.rt->siz;
int ret=0;
for(int i=1<<20;i;i>>=1)
if(chk(p1, p2, ret+i, len)) ret+=i;
return ret;
}
void solve()
{
tr.rt=0;
int m;
cin>>s;
for(auto c:s) tr.push_back(c);
cin>>m;
while(m--)
{
char op, c;
int p, x;
cin>>op>>p;
if(op=='I') cin>>c, tr.insert(p, c);
if(op=='R') cin>>c, tr.modify(p, c);
if(op=='Q') cin>>x, cout<<LCP(p, x)<<'\n';
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
lev[0]=1;
for(int i=1;i<maxn;i++) lev[i]=lev[i-1]*lx;
int t;
cin>>t;
while(t--) solve();
}

本文作者:Jimmy-LEEE

本文链接:https://www.cnblogs.com/redacted-area/p/18379538

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Jimmy-LEEE  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起