后缀数据结构太TM毒瘤了
给定一个字符串s,大小为n,下标从1开始。
sa[i]代表排名为i的后缀是哪一个。
rk[i]代表第i个后缀的排名,满足rk[sa[i]]=i。
h[i]代表第i个后缀的"高度",满足h[i]=height[rk[i]]
height[i]代表排名为i的后缀与排名为i-1的后缀的lcp,即后缀树上lca的深度。
下面先用倍增+基数排序法求出后缀数组sa。
然后求出sa的反数组rk。
h数组有一个性质:h[i]>=h[i-1]-1。证明:大概是利用后缀树上某一个子树内存储信息比深度较浅的相似的树存储信息少来证明,具体忘了。
利用这个性质可以在线形时间复杂度内求出h数组。
然后利用sa和rk数组即可求出height数组。
在height数组中,排名为i的后缀和排名为j的后缀的(i!=j)lcp为min(height[i+1],height[i+2],...,height[j-1],height[j])
这是一个rmq问题,如果需要O(1)求出两个后缀的lcp,可以将height数组进行倍增ST处理。
int n;
char s[300010];
int sa[300010], t1[300010], t2[300010], c[300010];
int rk[300010], height[300010], h[300010]; //height[i] = lca(i - 1, i)
void build(int m)
{
int *x = t1, *y = t2;
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) c[x[i] = s[i]]++;
for (int i = 1; i <= m; i++) c[i] += c[i - 1];
for (int i = n; i >= 1; i--) sa[c[x[i]]--] = i;
for (int k = 1; k <= n; k <<= 1)
{
int p = 0;
for (int i = n - k + 1; i <= n; i++) y[++p] = i;
for (int i = 1; i <= n; i++) if (sa[i] > k) y[++p] = sa[i] - k;
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) c[x[i]]++;
for (int i = 1; i <= m; i++) c[i] += c[i - 1];
for (int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i];
swap(x, y), p = 1, x[sa[1]] = 1;
for (int i = 2; i <= n; i++) x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) ? p : ++p;
if (p >= n) break;
m = p;
}
}
int main()
{
scanf("%d%s", &n, s + 1);
build(2333);
for (int i = 1; i <= n; i++) rk[sa[i]] = i;
for (int i = 1; i <= n; i++)
{
h[i] = h[i - 1] - (h[i - 1] > 0);
while (h[i] <= n && s[i + h[i]] == s[sa[rk[i] - 1] + h[i]]) h[i]++;
height[rk[i]] = h[i];
}
}