以前觉得这题好难,现在觉得这题还是挺简单
首先看到类似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.