• bzoj3230


    以前觉得这题好难,现在觉得这题还是挺简单
    首先看到类似LCP问题不难想到后缀数组吧
    前后的相似需要我们分别做一个后缀数组和“前缀数组”(就是把字符串反向然后跑后缀数组)
    这道题的难点就在于如何确定子串是什么
    考虑到一个有用的结论:任何一个子串都是某一个后缀的某一个前缀
    由于做完后缀数组之后,后缀已经按照从小到大排列了
    因此考虑相邻名次后缀sa[i]和sa[i-1],然后在这两个后缀间,还夹着一些子串
    不难发现,这些子串就是后缀sa[i]从第h[i]+1位开始的前缀
    为什么是从h[i]+1开始的前缀呢,因为前缀1~h[i]在一定已经在后缀sa[i-1]出现过了,要避免重复
    因此,我们可以计算相邻两个后缀之间不同的子串数目,然后维护前缀和
    这样就可以用二分来确定名词为x的子串是哪个后缀的前缀
    然后求相似程度不难想到用ST解决 利用LCP(i,j)=min(height[rank[i]+1~rank[j]]) 假定rank[i]<rank[j]
    注意这道题名次可能会爆longint,我一开始都想到子串数目会爆longint,可读入的时候竟然忘了用int64,囧……~

      1 var sa,rank,h:array[1..2,0..120010] of longint;
      2     f:array[1..2,0..100010,0..20] of longint;
      3     x,y:array[0..120010] of longint;
      4     d:array[0..20] of longint;
      5     sum:array[0..100010] of int64;
      6     s1,s2:ansistring;
      7     i,n,t:longint;
      8     p,q:int64;
      9 
     10 function min(a,b:int64):int64;
     11   begin
     12     if a>b then exit(b) else exit(a);
     13   end;
     14 
     15 procedure swap(var a,b:longint);
     16   var c:longint;
     17   begin
     18     c:=a;
     19     a:=b;
     20     b:=c;
     21   end;
     22 
     23 procedure suffix(s:ansistring;k:longint);
     24   var m,i,j:longint;
     25   begin
     26     fillchar(sum,sizeof(sum),0);
     27     for i:=1 to n do
     28     begin
     29       y[i]:=ord(s[i]);
     30       inc(sum[y[i]]);
     31     end;
     32     for i:=2 to 127 do
     33       inc(sum[i],sum[i-1]);
     34     for i:=n downto 1 do
     35     begin
     36       sa[k,sum[y[i]]]:=i;
     37       dec(sum[y[i]]);
     38     end;
     39     p:=1;
     40     rank[k,sa[k,1]]:=1;
     41     for i:=2 to n do
     42     begin
     43       if (y[sa[k,i]]<>y[sa[k,i-1]]) then inc(p);
     44       rank[k,sa[k,i]]:=p;
     45     end;
     46     m:=p;
     47     j:=1;
     48     while m<n do
     49     begin
     50       y:=rank[k];
     51       fillchar(sum,sizeof(sum),0);
     52       p:=0;
     53       for i:=n-j+1 to n do
     54       begin
     55         inc(p);
     56         x[p]:=i;
     57       end;
     58       for i:=1 to n do
     59         if sa[k,i]>j then
     60         begin
     61           inc(p);
     62           x[p]:=sa[k,i]-j;
     63         end;
     64 
     65       for i:=1 to n do
     66       begin
     67         rank[k,i]:=y[x[i]];
     68         inc(sum[rank[k,i]]);
     69       end;
     70       for i:=2 to m do
     71         inc(sum[i],sum[i-1]);
     72       for i:=n downto 1 do
     73       begin
     74         sa[k,sum[rank[k,i]]]:=x[i];
     75         dec(sum[rank[k,i]]);
     76       end;
     77       p:=1;
     78       rank[k,sa[k,1]]:=1;
     79       for i:=2 to n do
     80       begin
     81         if (y[sa[k,i]]<>y[sa[k,i-1]]) or (y[sa[k,i]+j]<>y[sa[k,i-1]+j]) then inc(p);
     82         rank[k,sa[k,i]]:=p;
     83       end;
     84       m:=p;
     85       j:=j shl 1;
     86     end;
     87     h[k,1]:=0;
     88     p:=0;
     89     for i:=1 to n do
     90     begin
     91       if rank[k,i]=1 then continue;
     92       j:=sa[k,rank[k,i]-1];
     93       while (i+p<=n) and (j+p<=n) and (s[i+p]=s[j+p]) do inc(p);
     94       h[k,rank[k,i]]:=p;
     95       if p>0 then dec(p);
     96     end;
     97   end;
     98 
     99 procedure rmq(k:longint);
    100   var t,i,j:longint;
    101   begin
    102     for i:=1 to n do
    103       f[k,i,0]:=h[k,i];
    104     t:=trunc(ln(n)/ln(2));
    105     for j:=1 to t do
    106       for i:=1 to n do
    107         if (i+d[j]-1<=n) then
    108           f[k,i,j]:=min(f[k,i,j-1],f[k,i+d[j-1],j-1])
    109         else break;
    110   end;
    111 
    112 function ask(x,y,k:longint):longint;
    113   var t:longint;
    114   begin
    115     if x=y then exit(n+1-sa[k,x]);
    116     if x>y then swap(x,y);
    117     inc(x);
    118     t:=trunc(ln(y-x+1)/ln(2));
    119     exit(min(f[k,x,t],f[k,y-d[t]+1,t]));
    120   end;
    121 
    122 function find(x:int64):longint;
    123   var l,m,r:longint;
    124   begin
    125     l:=0;
    126     r:=n;
    127     while l<=r do
    128     begin
    129       m:=(l+r) shr 1;
    130       if (sum[m-1]<x) and (sum[m]>=x) then exit(m);
    131       if sum[m]<x then l:=m+1 else r:=m-1;
    132     end;
    133   end;
    134 
    135 function getans(x,y:int64):int64;
    136   var a,b,aa,bb,c:longint;
    137   begin
    138     a:=find(x);  //查找是哪个后缀的前缀
    139     b:=find(y);
    140     aa:=sa[1,a]+h[1,a]+x-sum[a-1]-1;  //定位前缀数组中的位置
    141     bb:=sa[1,b]+h[1,b]+y-sum[b-1]-1;
    142     aa:=rank[2,n+1-aa];  
    143     bb:=rank[2,n+1-bb];
    144     c:=min(h[1,a]+x-sum[a-1],h[1,b]+y-sum[b-1]);  //注意这里我们是求子串所在后缀的LCP,可能会超出原来子串的长度
    145     getans:=sqr(min(c,ask(a,b,1)))+sqr(min(c,ask(aa,bb,2)));
    146   end;
    147 
    148 begin
    149   readln(n,t);
    150   readln(s1);
    151   s2:='';
    152   for i:=n downto 1 do
    153     s2:=s2+s1[i];
    154   suffix(s1,1);
    155   suffix(s2,2);
    156   d[0]:=1;
    157   for i:=1 to trunc(ln(n)/ln(2)) do
    158     d[i]:=d[i-1]*2;
    159   rmq(1);  //处理ST
    160   rmq(2);
    161   sum[0]:=0;
    162   for i:=1 to n do
    163     sum[i]:=sum[i-1]+n+1-sa[1,i]-h[1,i];
    164   for i:=1 to t do
    165   begin
    166     readln(p,q);
    167     if (p>sum[n]) or (q>sum[n]) then  //判断是否超出子串数目
    168     begin
    169       writeln(-1);
    170       continue;
    171     end;
    172     writeln(getans(p,q));
    173   end;
    174 end.
    View Code
  • 相关阅读:
    Kali 查看系统信息的一些命令及查看已安装软件包的命令
    mysql_对于DQL 的简单举例
    java简单分析LinkedList
    java_简单解析ArrayList_iterable
    java_随机密码
    rsync 服务基础配置讲解
    DNS服务器的基础
    NFS服务器配置
    DHCP服务器配置
    VSFTP 配置详解,附带例子
  • 原文地址:https://www.cnblogs.com/phile/p/4473086.html
Copyright © 2020-2023  润新知