火星人的研究 Prefix

【题意】

对一个字符串实现三种操作

Q i j:询问该字符串的后缀i和后缀j的最长公共前缀长度

R i c:将当前字符串第i位变为小写字母c

I i c:在当前字符串第i位后插入一个小写字母c

【输入】
输入文件第一行为一个字符串(仅包含小写字母)。
接下来的一行的整数Q 代表操作的个数(1 ≤Q ≤15000)。
接下来Q 行表示了待执行的操作。
Q i j 表示求LCP(i, j)的数值
R i char 表示将第i 位置的字符替换成小写字母char
I i char 表示在第i 个字符后插入小写字母char

【输出】
对于每一个Q i j,输出一行为LCP(i, j)的数值。


最初看到这道题在想是不是对于修改可以通过修改后缀数组实现

发现不可行

ac做法是hash

求最长公共前缀可以通过二分答案来变为存在性问题

那么只要比较二者的hash值即可

虽然有一定概率不同的字符串hash为相同的值,但是只要方程选取的当出错的几率还是很小的

那么问题就转换为了如何快速求一个字符串子串的hash值

采用的hash方程为长度为n的字符串的hash值为(a[1]*k^0+a[2]*k^1+a[3]*k^2..+a[n]*k^(n-1)) mod maxn

如果用这个方程的话,那么一段的hash可以通过两段直通末尾的字符串hash值相减得出

我才用了splay树来维护快速查询

对于每个字符将其放入树中,其左子树中的字符都是比其位置靠前的,其右子树中的字符都是比其位置靠后的

每个节点的权值h为其子树(包括本身)构成的原字符串的子串的hash值,并记录子树的节点总数sum

那么询问原字符串一个后缀的hash值只需要将他所在的节点上移到根节点处,这时他(它本身代表的字符序号+(其右子树权值*k))即为这个后缀的hash值

对于变换,只要将改变的节点移到根节点之后修改hash值即可

对于插入,将要插入位置的前一个移到根节点后插到右子树即可

splay我不太喜欢,因为不是稳定结构,据说可以用块状链表来做,想了想似乎是可以的,将原字符串分为根号n段?具体做法不太清楚。


{$inline on}
program prefix;
const
  maxn=9875321;
  k=27;
var
  tot,count,a,b,ans,q,n,i,j:longint;
  s:ansistring;
  ch,order,space:char;
  h:array [0..100011] of int64;
  power:array [0..100011] of int64;
  nam,father,left,right,sum:array [0..200011] of longint;

procedure update (now:longint);inline;
begin
  h[now]:=(h[left[now]]+power[sum[left[now]]]*nam[now]
  +power[sum[left[now]]+1]*h[right[now]]) mod maxn;
end;

procedure build (l,r,mother:longint);
var
  now:longint;
begin
  if l>r then exit;
  now:=(l+r) div 2;
  nam[now]:=ord(s[now])-96;
  sum[now]:=r-l+1;
  if now>mother then right[mother]:=now
                else left[mother]:=now;
  father[now]:=mother;
  build(l,now-1,now);
  build(now+1,r,now);
  update(now);
end;

function find (lef,now:longint):longint;
begin
  if sum[left[now]]+1=lef then exit(now)
                          else
  if sum[left[now]]+1<lef then exit(find(lef-(sum[left[now]]+1),right[now]))
                          else exit(find(lef,left[now]));
end;

procedure rotate (son,mother:longint);
begin
  father[son]:=father[mother];
  father[mother]:=son;
  if left[father[son]]=mother then left[father[son]]:=son
                              else right[father[son]]:=son;
  if left[mother]=son then
    begin
      left[mother]:=right[son];
      father[right[son]]:=mother;
      right[son]:=mother;
    end
                      else
    begin
      right[mother]:=left[son];
      father[left[son]]:=mother;
      left[son]:=mother;
    end;
  sum[son]:=sum[mother];
  sum[mother]:=sum[left[mother]]+sum[right[mother]]+1;
  update(mother);
  update(son);
end;

procedure rep (who:longint;what:char);
var
  i:longint;
begin
  i:=find(who,right[0]);
  while father[i]<>0 do rotate(i,father[i]);
  nam[i]:=ord(what)-96;
  update(i);
end;

procedure ins (who:longint;what:char);
var
  i:longint;
begin
  i:=find(who,right[0]);
  while father[i]<>0 do rotate(i,father[i]);
  inc(n);
  nam[n]:=ord(what)-96;
  right[n]:=right[i];
  father[right[i]]:=n;
  father[n]:=i;
  right[i]:=n;
  inc(sum[i]);
  sum[n]:=sum[right[n]]+1;
  update(n);
  update(i);
end;

function hash (a,b:longint):longint;
var
  i,j:int64;
begin
  i:=find(a,right[0]);
  while father[i]<>0 do rotate(i,father[i]);
  i:=nam[i]+k*h[right[i]];
  if b>n then j:=0
         else
    begin
      j:=find(b,right[0]);
      while father[j]<>0 do rotate(j,father[j]);
      j:=nam[j]+k*h[right[j]];
    end;
  exit(((i-j*power[b-a] mod maxn) mod maxn + maxn) mod maxn);
end;

function lcq (a,b:longint):longint;
var
  i,j,l,r,mid:longint;
begin
  if nam[find(a,right[0])]<>nam[find(b,right[0])] then exit(0);
  l:=1;
  if n-a+1<n-b+1 then r:=n-a+1
                 else r:=n-b+1;
  while l<>r do
    begin
      mid:=l+r-(l+r) div 2;
      if hash(a,a+mid)=hash(b,b+mid) then l:=mid
                                     else r:=mid-1;
    end;
  exit(l);
end;

begin
  assign(input,'prefix.in');
  reset(input);
  assign(output,'prefix.out');
  rewrite(output);
  readln(s);
  n:=length(s);
  power[0]:=1;
  for i:=1 to 100010 do
    power[i]:=power[i-1]*k mod maxn;
  build(1,n,0);
  readln(q);
  for i:=1 to q do
    begin
      read(order);
      case order of
        'Q':begin
              read(a,b);
              writeln(lcq(a,b));
            end;
        'R':begin
              read(a,space,ch);
              rep(a,ch);
            end;
        'I':begin
              read(a,space,ch);
              ins(a,ch);
            end;
      end;
      readln;
    end;
  close(input);
  close(output);
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值