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
原文地址:https://www.cnblogs.com/phile/p/4473086.html