[JSOI2008]火星人prefix

本文探讨了在给定字符串上进行修改和插入操作的同时,如何快速计算两个后缀的公共前缀长度(LCQ函数)。通过动态维护字符串的后缀排序和使用哈希+二分查找的方法,实现高效求解。详细介绍了Splay树的运用,并提供了AC代码,适用于解决插入、修改和询问操作的复杂问题。

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

Time Limit: 10 Sec  Memory Limit: 162 MB

Description

火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串,两个字串的公共前缀的长度。比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0 在研究LCQ函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出LCQ函数的值;同样,如果求出了LCQ函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取LCQ函数的快速算法,但不甘心认输的地球人又给火星人出了个难题:在求取LCQ函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此复杂的问题中,火星人是否还能够做到很快地求取LCQ函数的值。

Input

第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。接下来的M行,每行描述一个操作。操作有3种,如下所示: 1、 询问。语法:Q x y,x, y均为正整数。功能:计算LCQ(x, y) 限制:1 <= x, y <= 当前字符串长度。 2、 修改。语法:R x d,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字符串长度。 3、 插入:语法:I x d,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x = 0,则在字符串开头插入。限制:x不超过当前字符串长度。

Output

对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。

Sample Input

7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11

Sample Output

5
1
0
2
1
数据规模:
对于100%的数据,满足:
1、 所有字符串自始至终都只有小写字母构成。
2、 M <= 150,000
3、 字符串长度L自始至终都满足L <= 100,000
4、 询问操作的个数不超过10,000个。

对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
题意:有LCQ(x,y)操作询问以当前字符串中第x个开始和第y个开始的最长公共前缀的长度。同时还可以插入一个字符或者修改一个字符。
询问最大长度,想到for。。。但同时想到会超时。。。同时字符串比较灰常慢。。。所以可以用hash+二分查找最长公共前缀。我们可以设计一个较好的hash函数,让每个不同的字符串的hash值尽量不重复。因为有插入和修改序列的操作。所以就要动态维护。用到了splay。
然后每个树中的节点记录自己的父亲fa[i],子节点c[i,0]c[i,1],子树(包括本身)构成的字符串的hash值h[i],本身字符a[i],子树(包括自己)中的节点个数sum[i]。
hash的求法:[ord(a[1])*k^0+ord(a[2])*k^1+ord(a[3])*k^2+...+ord(a[n])*k^(n-1)] mod maxn
那在旋转的时候怎么修改h[i]呢?h[x]:=h[c[x,0]]+a[x]*k^(sum[c[x,0]])+h[c[x,1]]*k^(sum[c[x,0]]+1)
每次二分长度只要把要匹配的子串转成根的右节点的左子树所对应的串。比较两个两棵子树的hash值是否相同判断子串是否相同。
可以加几个特判:
1.判断首字母位置是否相同。相同输出最大长度即可。
2.判断首字母是否是不同字母。不同则输出0。
3.最大长度特判。有的数据整列字符都是一样的。。。。
 
AC CODE
program hy_1014;
const maxn=9875321;
var h:array[0..100010] of int64;
    d:array[1..100010] of int64;
    a,sum:array[0..100010] of int64;
    fa:array[0..100010] of longint;
    ll,rr:array[1..150010] of longint;
    c:array[0..100010,0..1] of longint;
    size,k,kk,root,n,m,all:longint;
    hh,ch:array[1..150010] of char;
//============================================================================
procedure update(x:longint);
var p,q:longint;
begin
  sum[x]:=sum[c[x,0]]+sum[c[x,1]]+1;
  p:=c[x,0]; q:=c[x,1];
  h[x]:=(h[p]+a[x]*d[sum[p]+1]+h[q]*d[sum[p]+2]) mod maxn;   //更新hash值。
end;
//============================================================================
procedure build(l,r:longint);   //一开始用递归建树,防止树的深度太大。
var mid,tmp:longint;
begin
  mid:=(l+r) shr 1;
  if mid>l then
  begin
    build(l,mid-1);
    tmp:=(l+mid-1) shr 1;
    c[mid,0]:=tmp; fa[tmp]:=mid;
  end;
  if mid<r then
  begin
    build(mid+1,r);
    tmp:=(mid+1+r) shr 1;
    c[mid,1]:=tmp; fa[tmp]:=mid;
  end; update(mid);
end;
//============================================================================
procedure init;
var i,len:longint;
    st:ansistring;
    chh:char;
begin
  readln(st); len:=length(st);
  for i:=2 to len+1 do  //在头尾预留两个单元,splay时就总能找到l-1和r+1。
  begin
    a[i]:=ord(st[i-1])-ord('a');
    h[i]:=a[i];
  end; d[1]:=1;
  for i:=2 to 100010 do
    d[i]:=(d[i-1]*27) mod maxn;   //预处理k的n次方(见hash函数)。
  build(1,len+2); root:=(len+3) shr 1;
  readln(m); size:=len+2;
  for i:=1 to m do
  begin
    read(ch[i]);
    if ch[i]='Q' then
    begin
      inc(all); readln(ll[i],rr[i]);
    end else readln(ll[i],chh,hh[i]);
  end;
end;
//============================================================================
function find(x:longint):longint;
begin
  if sum[c[x,0]]+1=k then exit(x);
  if sum[c[x,0]]<k-1 then
  begin
    dec(k,sum[c[x,0]]+1);
    find:=find(c[x,1]);
  end else find:=find(c[x,0]);
end;
//============================================================================
procedure rotate(var root:longint; x:longint);
var y,z,p,q:longint;
begin
  y:=fa[x]; z:=fa[y];
  if c[y,0]=x then p:=0 else p:=1;
  q:=p xor 1;
  if y=root then root:=x else
    if c[z,0]=y then c[z,0]:=x else c[z,1]:=x;
  fa[x]:=z; fa[y]:=x; fa[c[x,q]]:=y;
  c[y,p]:=c[x,q]; c[x,q]:=y;
  update(y); update(x);
end;
//============================================================================
procedure splay(var root:longint; x:longint);
var y,z:longint;
begin
  while x<>root do
  begin
    y:=fa[x]; z:=fa[y];
    if y<>root then
      if (c[y,0]=x) xor (c[z,0]=y) then
      rotate(root,x) else rotate(root,y);
    rotate(root,x);
  end;
end;
//============================================================================
procedure insert(x:longint);
begin
  if x=0 then
  begin
    inc(size); h[size]:=kk; sum[size]:=1;
    a[size]:=h[size]; exit;
  end;
  if sum[c[x,0]]>=k then
  begin
    insert(c[x,0]);
    if c[x,0]=0 then begin c[x,0]:=size; fa[size]:=x; end;
  end else
  begin
    k:=k-sum[c[x,0]]-1; insert(c[x,1]);
    if c[x,1]=0 then begin c[x,1]:=size; fa[size]:=x; end;
  end; update(x);
end;
//============================================================================
procedure ask(x:longint);
var xx,yy,l,r,ans,l1,l2,left1,left2:longint;
    hash1,hash2,s,t,mid,tt:longint;
begin
  if rr[x]<ll[x] then
  begin
    tt:=rr[x]; rr[x]:=ll[x]; ll[x]:=tt;
  end;
  if ll[x]=rr[x] then
  begin writeln(size-1-ll[x]); exit; end;
  k:=ll[x]; l:=find(root); splay(root,l);
  k:=rr[x]; r:=find(root);
  k:=ll[x]+1; xx:=find(root); k:=rr[x]+1; yy:=find(root);
  if a[xx]<>a[yy] then
  begin writeln('0'); exit; end;
  s:=1; t:=size-rr[x]-1; ans:=0;
    k:=ll[x]+t+1; l1:=find(root);
    k:=rr[x]+t+1; l2:=find(root);
    splay(c[root,1],l1); hash1:=h[c[l1,0]];
    splay(c[root,1],r); splay(c[r,1],l2); hash2:=h[c[l2,0]];
    if hash1=hash2 then begin writeln(t); exit; end; //这里特判了最长长度的串。。有的数据太猥琐。。。
  dec(t); 
  repeat
    mid:=(s+t) shr 1;
    k:=ll[x]+mid+1; l1:=find(root);
    k:=rr[x]+mid+1; l2:=find(root);
    splay(c[root,1],l1); hash1:=h[c[l1,0]];
    splay(c[root,1],r); splay(c[r,1],l2); hash2:=h[c[l2,0]];
    if hash1=hash2 then
    begin s:=mid+1; ans:=mid; end else t:=mid-1;
  until s>t; writeln(ans);
end;
//============================================================================
procedure rep(x:longint);
var z,p,q:longint;
begin
  k:=ll[x]+1; z:=find(root); splay(root,z);
  a[z]:=ord(hh[x])-ord('a'); p:=c[root,0]; q:=c[root,1];
  h[z]:=(h[p]+a[z]*d[sum[p]+1]+h[q]*d[sum[p]+2]);
end;
//============================================================================
procedure ins(x:longint);
begin
  k:=ll[x]+1; kk:=ord(hh[x])-ord('a'); insert(root);
  splay(root,size);
end;
//============================================================================
procedure work;
var i:longint;
begin
  for i:=1 to m do
  begin
    if ch[i]='Q' then
    begin
      ask(i); dec(all);
      if all=0 then halt;
    end else if ch[i]='R' then rep(i) else ins(i);
  end;
end;
//============================================================================
begin
  init;
  work;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值