前言
SA是一种很好处理字符串的工具。
举个栗子很快可以明白
假设我们要处理“aabbabb”这个字符串,最终将得到:
字符串 | a | a | b | b | a | b | b |
---|---|---|---|---|---|---|---|
rank | 1 | 3 | 7 | 5 | 2 | 6 | 4 |
sa | 1 | 5 | 2 | 7 | 4 | 6 | 3 |
一些关键数组:
- sa[i]:排名第i名的后缀子串在什么位置。
- rank[i]:第i位后缀子串排名第几。
-h[i]:排名第i位的后缀子串与排名第i-1位的后缀子串最长公共前缀。
因此,我们所要做的主要工作是求以上三个数组,而本文所介绍的即是倍增法。
何为倍增?
由于笔者比较懒,所以不会太详细说,最直观的理解方式就是看代码。
简而言之,就是利用字符串长度为2k−12k−1的信息(rank等)转移长度为2k2k的信息。
其他数组
-tp[i]:相当于排名第i位的“十位”在哪里(若当前求长度为2k2k,则储存当前位置前去2k−12k−1,是一个辅助数组)。
-tax[i]:桶排所用的“桶”。
Pascal代码:
const maxn=1000005;
var s:ansistring;
k,st:int64;
n,max:longint;
sa,rank,tp,h,tax:array[0..maxn] of longint;
procedure sort;//桶排
var i:longint;
begin
fillchar(tax,sizeof(tax),0);
for i:=1 to n do inc(tax[rank[tp[i]]]);
for i:=1 to max do inc(tax[i],tax[i-1]);
for i:=n downto 1 do begin //为什么从
//后往前呢?因为之前已经求好了“个位”的rank
sa[tax[rank[tp[i]]]]:=tp[i];
dec(tax[rank[tp[i]]]);
end;
end;
function cmp(x,y,z:longint):boolean;
begin
exit((tp[x]=tp[y]) and (tp[x+z]=tp[y+z]));
end;
procedure G_Y;
var k,i,j:longint;
begin
k:=0;
for i:=1 to n do begin
if k>0 then dec(k);
j:=sa[rank[i]-1];
if j=0 then continue;
while (i+k<=n) and (j+k<=n) and (s[i+k]=s[j+k]) do inc(k);
h[rank[i]]:=k;
end;
end;
procedure suffix;
var i,p,l:longint;
begin
sort;
l:=1;
while l<n do begin
for i:=1 to l do tp[i]:=n-i+1;
p:=l;
for i:=1 to n do if sa[i]>l then begin
inc(p);
tp[p]:=sa[i]-l;
end;
sort;
tp:=rank;
rank[sa[1]]:=1;
p:=1;
for i:=2 to n do if cmp(sa[i-1],sa[i],l) then
rank[sa[i]]:=p else begin//避免rank的重复
inc(p);
rank[sa[i]]:=p;
end;
max:=p;
l:=l*2;
end;
end;
procedure init;
var i:longint;
begin
readln(s);
readln(k);
n:=length(s);
for i:=1 to n do begin
rank[i]:=ord(s[i]);
if rank[i]>max then max:=rank[i];
tp[i]:=i;//初始化,把“十位”当做“个位”
end;
end;
begin
init;//
suffix;//求rank与sa的东西
G_Y;//求h的东西
end.