【参考博客】
【定义】
【后缀】从第i位到字符串结尾的子串
【解决问题】
从而解决
...................在字符串中找子串
...................比较子串关系
...................查找不同子串的数目
一般来说都是解决
字符串和子串关系的问题
【算法学习】
后缀数组能够在 nlogn的时间复杂度内求取出以下数组
SA [] 储存,第 i 个数字表示的是字典序第 i 大的后缀是以 SA_i 开始的后缀
即 “排第几的是哪个后缀”
rank [] 储存,从第i位开始的后缀的字典序排名是 i
即 “某个后缀排第几个”
对SA的求取,我们可以看作对所有的后缀进行排序
而这个排序显然如果直接莽的话肯定T,所以我们需要另外一种方法
这里使用基数排序+倍增的方法进行优化
将所有的后缀进行排序得到SA,这是我们的目的
基数排序,是对两个关键字的元素进行排序从而达到线性复杂度的方法
显然,在当两个后缀第一个字符相等的情况下,我们不可避免的去用第二个字符进行比较
这里我们需要注意到一个事情
第 i 个后缀的第二个字符是第 i + 1 的第一个字符
那这和直接莽好像没有什么区别?
所以我们就用到了倍增,每次确定后缀的以前 2 ^ k 长度的字符串排序的顺序
然后通过这个,就能够求出 2 ^ ( k + 1 ) 长度的后缀的前缀
通过,第 i 位的和第 i + k 位的,于是就能求出来
因为第一个字符有可能一样,导致有两个后缀排名相等
所以总排名数和后缀长度不同
所以长度为2时同理
而当所有总排名和后缀长度相同时,这个时候就找到了所有
下面是对使用到的各个数组的意义的描述:
c[] 桶,记录第 i 位的元素有多少个
x[] 后缀 i 的第一关键字,所以最开始是等于第 i 位字符
y[] 第二关键字排名第 i 的字符串,第一关键字的位置
【代码】

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 1000010; char s[MAXN]; int SA[MAXN]; int x[MAXN],c[MAXN],y[MAXN]; void get_SA(char *s) { int n = strlen(s+1); int m = 128; for (int i = 1; i <= n; i++) ++c[x[i] = s[i]]; //求前缀和 for (int i = 2; 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 num = 0; for (int i = n - k + 1; i <= n; i++) y[++num] = i; for (int i = 1; i <= n; i++) if (SA[i] > k) y[++num] = 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 = 2; i <= m; i++) c[i] += c[i - 1]; for (int i = n; i >= 1; i--) SA[c[x[y[i]]]--] = y[i], y[i] = 0; swap(x, y); x[SA[1]] = 1; num = 1; for (int i = 2; i <= n; i++) x[SA[i]] = (y[SA[i]] == y[SA[i - 1]] && y[SA[i] + k] == y[SA[i - 1] + k]) ? num : ++num; if (num == n) break; m = num; } return; } int main() { scanf("%s", s+1); get_SA(s); int n = strlen(s+1); for (int i = 1; i <= n; i++) printf("%d ", SA[i]); return 0; }