poj3415

【题意】

给定一个数k

在给定两个字符串A,B

求三元组(i,j,k)(表示从A的第i位起和从B的j位起长度为k的字符串相同)的个数

【输入】

多组数据

每组数据第一行为k

接下来两行分别为A、B(长度均不大于100000)

【输出】

对于每组数据,输出一个数,表示三元组的个数


后缀数组应用题之一

后缀数组的用法很经典

将两个字符串之间加一个没出现过的字符连接起来

然后求height

对于B的一个后缀,对应每一个A的后缀若他们的公共前缀长为l,若l大于等于k,则会有l-k+1种三元组

这个统计便是本题的难点

如果枚举A的后缀和B的后缀,那么复杂度为n^2,对于本题明显是不可以的

所以要另寻途径

根据论文的提示要使用单调栈,我想到了一种实现

按rank的顺序,统计每一个B的后缀名次之前的A的后缀有关的三元组数量

然后再统计每一个A的后缀名次之前的B的后缀有关的三元组数量

两者之和便是答案

将问题划分为了两个等价的问题

那么完成每一个问题的时候从左到右扫描,每一个只跟已扫描过的有关

这时候便可以动态维护了

首先,与许多后缀数组题目中类似的,这里存在三元组的后缀们是聚集在一起的

按height可以分组

对于每一组从左到右扫描,则需要维护一个栈和一个值

这个栈是栈内元素与当前元素公共前缀长度递增的一个栈,值是若当前当前后缀之前的三元组个数

根据最长公共前缀的性质,rank越相近,则公共前缀长度越大,所以从左到右扫描的后缀与当前后缀的公共前缀长度是递减的

栈内每个元素表示之前有total个后缀与当前元素公共前缀长度为sim,明显,若当前height小于某些栈内元素的sim,则需要修改这些元素和值

据此调整,每个元素最多进出栈一次,复杂度为O(N)


program poj3415;
var
  n,m,k,i,j,l,tot:longint;
  a,b,ans:int64;
  root,temp:ansistring;
  dl,change,total,now,keep,sa,rank,height:array [-1..200001] of longint;
  xl:array [0..200001] of record
                            total,sim:longint;
                          end;
  min:array [0..19,0..200001] of longint;

procedure swap (var a,b:longint);
var
  i:longint;
begin
  i:=a;
  a:=b;
  b:=i;
end;

procedure qsort (s,e:longint);
var
  i,j,k:longint;
begin
  if s>=e then exit;
  i:=s;
  j:=e;
  k:=dl[(s+e) div 2];
  while i<=j do
    begin
       while root[dl[i]]<root[k] do inc(i);
       while root[dl[j]]>root[k] do dec(j);
       if i>j then break;
       swap(dl[i],dl[j]);
       inc(i);
       dec(j);
    end;
  qsort(s,j);
  qsort(i,e);
end;

procedure st;
begin
  for i:=1 to n do
    min[0,i]:=height[i];
  k:=0;
  while 1 shl k<n do
    begin
      for i:=1 to n do
        if i + (1 shl k)>n then min[k+1,i]:=min[k,i]
                           else
        if min[k,i]<min[k,i + (1 shl k)] then min[k+1,i]:=min[k,i]
                                         else min[k+1,i]:=min[k,i + (1 shl k)];
      inc(k);
    end;
end;

begin
  repeat
    readln(l);
    if l=0 then break;
    readln(root);
    a:=length(root);
    readln(temp);
    b:=length(temp);
    root:=root+' '+temp;
    n:=a+b+1;
    m:=a+1;
    for i:=1 to n do
      dl[i]:=i;
    qsort(1,n);
    k:=1;
    for i:=1 to n do
      begin
        if root[dl[i]]<>root[dl[k]] then k:=i;
        rank[dl[i]]:=k;
      end;
    k:=0;
    while 1 shl k<n do
      begin
        for i:=1 to n do
          if i+(1 shl k)>n then change[i]:=0
                           else change[i]:=rank[i+(1 shl k)];
        fillchar(total,sizeof(total),0);
        for i:=1 to n do
          inc(total[change[i]]);
        for i:=1 to n do
          total[i]:=total[i]+total[i-1];
        for i:=1 to n do
          begin
            dl[total[change[i]-1]+1]:=i;
            inc(total[change[i]-1]);
          end;
        fillchar(total,sizeof(total),0);
        for i:=1 to n do
          inc(total[rank[i]]);
        for i:=2 to n do
          total[i]:=total[i]+total[i-1];
        fillchar(now,sizeof(now),0);
        fillchar(keep,sizeof(keep),0);
        for i:=1 to n do
          begin
            if now[rank[dl[i]]]<>change[dl[i]] then
              begin
                now[rank[dl[i]]]:=change[dl[i]];
                total[rank[dl[i]]-1]:=total[rank[dl[i]]-1]+keep[rank[dl[i]]];
                keep[rank[dl[i]]]:=0;
              end;
            inc(keep[rank[dl[i]]]);
            rank[dl[i]]:=total[rank[dl[i]]-1]+1;
          end;
        inc(k);
      end;
    for i:=1 to n do
      sa[rank[i]]:=i;
    fillchar(height,sizeof(height),0);
    for i:=1 to n do
      begin
        if rank[i]=1 then continue;
        k:=height[rank[i-1]]-1;
        if k<0 then k:=0;
        while (i+k<=n)and(sa[rank[i]-1]+k<=n)
        and(root[i+k]=root[sa[rank[i]-1]+k]) do inc(k);
        height[rank[i]]:=k;
      end;
    ans:=0;
    tot:=0;
    a:=0;
    for i:=3 to n do
      begin
        if height[i]<l then
          begin
            tot:=0;
            a:=0;
            continue;
          end;
        if sa[i-1]<m then b:=1
                     else b:=0;
        while (tot>0)and(height[i]<=xl[tot].sim) do
          begin
            b:=b+xl[tot].total;
            a:=a-(xl[tot].sim-l+1)*xl[tot].total;
            dec(tot);
          end;
        a:=a+(height[i]-l+1)*b;
        if b<>0 then
          begin
            inc(tot);
            xl[tot].sim:=height[i];
            xl[tot].total:=b;
          end;
        if sa[i]>m then ans:=ans+a;
      end;
    tot:=0;
    a:=0;
    for i:=3 to n do
      begin
        if height[i]<l then
          begin
            tot:=0;
            a:=0;
            continue;
          end;
        if sa[i-1]>m then b:=1
                     else b:=0;
        while (tot>0)and(height[i]<=xl[tot].sim) do
          begin
            b:=b+xl[tot].total;
            a:=a-(xl[tot].sim-l+1)*xl[tot].total;
            dec(tot);
          end;
        a:=a+(height[i]-l+1)*b;
        if b<>0 then
          begin
            inc(tot);
            xl[tot].sim:=height[i];
            xl[tot].total:=b;
          end;
        if sa[i]<m then ans:=ans+a;
      end;
    writeln(ans);
  until false;
end.


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值