[Noi2011]阿狸的打字机

这道题由于读入的字符串长度可能很大,不能一个一个字符串插入trie图。

又因为一个字符串形成以后字母不会消失,于是便可以一边读入一边建trie图。

然后,由于fail指针指向最长后缀,就可以将fail指针改变方向得到一棵fail树(因为每个点的出度为1,改了方向以后入度也为1)。在fail树中,一个单词节点的子树有多少个节点就代表这个单词在其他单词中出现了多少次。

如何快速查询一个节点的子树中有多少目标节点呢?

显然,先标记目标节点再用dfs先序遍历加树状数组这种O(nlogn)级别的方法比较快。

面对题目里如此大量的询问,离线做法会快很多。

但是如果一个字符串一个字符串地标记很明显会超时,怎么办呢?

联想到前面建trie图的做法,于是得到一个比较好的方法:

读入一个字母就在树状数组中相应的位置加1,删去一个字母就减1;遇到‘P’就处理y为当前字符串的询问。

这道题就可以解决了。

代码:

{$inline on}
type
  zy=record
    next:longint;
    ch:char;
end;
var
wei,l,r,a,d,fa:array [0..100000]of longint;
pre3,now3,son3,poss,pre2,now2,son2,pos,pre,now,son:array[0..100000]of longint;
tree:array[0..80000]of zy;
ss:array[0..100000]of char;
ans:array[0..100000] of longint;
sum,num,xx,w,lengg,p,x,leng,tot,tott,n,i,j,k:longint;
chh:char;
procedure insert(a:longint;chh:char);inline;//建trie图的边
begin
inc(tot); inc(tott);
pre[tot]:=now[a];
now[a]:=tot;
son[tot]:=tott;
tree[tott].ch:=chh;
fa[tott]:=a;
end;
 
procedure insert2(a,b:longint);inline;//建fail树的边
begin
inc(tot);
pre2[tot]:=now2[a];
now2[a]:=tot;
son2[tot]:=b;
end;
 
function check(x:longint;chh:char):longint;inline;//找到x的chh儿子
var
p:longint;
begin
p:=now[x];
while p<>0 do
  begin
    if tree[son[p]].ch=chh then
      exit(son[p]);
    p:=pre[p];
  end;
exit(0);
end;
 
procedure bfs;inline;//建trie图
var
kk,ffa,x,p,t,w,i,j,k:longint;
begin
d[1]:=1; t:=1; w:=1;tree[1].next:=1;
while t<=w do
  begin
    x:=d[t];
    p:=now[x];
    while p<>0 do
      begin
        inc(w);
        d[w]:=son[p];
        p:=pre[p];
      end;
    if x<>1 then
      begin
        ffa:=fa[x];
        while ffa<>1 do
          begin
            k:=check(tree[ffa].next,tree[x].ch);
            if k<>0 then
              begin
                tree[x].next:=k;
                insert2(k,x);
                break;
              end;
            ffa:=tree[ffa].next;
          end;
        if ffa=1 then
          begin
            tree[x].next:=1;
            insert2(1,x);
          end;
      end;
    inc(t);
  end;
end;
 
procedure dfs(x,fa:longint);//得出dfs序
var
ll,rr,p:longint;
begin
p:=now2[x];
inc(w); pos[x]:=w;
l[x]:=w;
while p<>0 do
  begin
    if son2[p]<>fa then
        dfs(son2[p],x);
    p:=pre2[p];
  end;
r[x]:=w;
end;
 
function lowbit(x:longint):longint;inline;
begin
exit(x and(-x));
end;
 
function find(x:longint):longint;inline;
var
ans:longint;
begin
ans:=0;
while x>0 do
  begin
    ans:=ans+a[x];
    x:=x-lowbit(x);
  end;
exit(ans);
end;
 
procedure add(x,v:longint);inline;
begin
while x<=tott do
  begin
    a[x]:=a[x]+v;
    x:=x+lowbit(x);
  end;
end;
 
begin
tott:=1;  x:=1;
while not eoln do
  begin
    read(chh);
    inc(lengg);
    ss[lengg]:=chh;
    if (chh<='z')and(chh>='a') then
      begin
        j:=check(x,chh);
        if j=0 then
          begin
            insert(x,chh);
            x:=tott;
          end
        else
          x:=j;
      end
    else
      if chh='B' then
        x:=fa[x]
      else
        begin
          inc(sum);
          wei[sum]:=x;
        end;
  end;
tot:=0;
bfs;
dfs(1,0);
readln;
readln(n);
for i:=1 to n do
  poss[i]:=i;
for i:=1 to n do
begin
  readln(j,k);
  son3[i]:=j; pre3[i]:=now3[k]; now3[k]:=i;
end;
x:=1;    num:=0;
for i:=1 to lengg do
  begin
    if (ss[i]<='z')and(ss[i]>='a') then
      begin
        x:=check(x,ss[i]);
        add(pos[x],1);
      end
    else
      if ss[i]='B' then
        begin
          add(pos[x],-1);
          x:=fa[x];
        end
      else
        begin
          inc(num); j:=now3[num];
          while j<>0 do
            begin
              ans[j]:=find(r[wei[son3[j]]])-find(l[wei[son3[j]]]-1);
              j:=pre3[j];
            end;
        end;
  end;
for i:=1 to n do
 writeln(ans[i]);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值