标签:lin 输入 数据 com nlogn 没有 ascii turn bit
读入一个长度为 n的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。位置编号为 1 到 n。
一行一个长度为 n 的仅包含大小写英文字母或数字的字符串。
一行,共n个整数,表示答案。
ababa
5 3 1 4 2
\(n\leq 10^6\)
#include <bits/stdc++.h>
const int maxn = 1e6 + 10;
char s[maxn];
int x[maxn], y[maxn], sa[maxn], c[512], rk[maxn];
int n, m;
void SA() {
//第一遍鸡排,以字母作关键字
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;
//y[i] 代表第二关键字排名为i的,在以第一关键字排序的排名
//不难发现,从n-k+1到n这些位置的后缀是没有第二关键字的
//所以字典序小,放进y里
for(int i = 1; i <= n; i++) if(sa[i] > k) y[++num] = sa[i] - k;
//排名为i的数 在数组中是否在第k位以后
//如果满足(sa[i]>k) 那么它可以作为别人的第二关键字,就把它的第一关键字的位置添加进y就行了
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];
//y是在第一关键字的排名,套上个x就是以第二关键字排序排名,再套上个c,就是分配排名
//因为y的顺序是按照第二关键字的顺序来排的
//第二关键字靠后的,在同一个第一关键字桶中排名越靠后
for(int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
std::swap(x, y);
//此时x是0了,因为生成新的x需要旧的,就是一个临时代替作用
//因为sa[i]已经排好序了,所以可以按排名枚举,生成下一次的第一关键字
//重新排名
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;
//下次用的不是ascii了,用的是排名,所以改变m
m = num;
}
for(int i = 1; i <= n; i++) printf("%d%c", sa[i], i == n? '\n' : ' ');
}
int main() {
scanf("%s", s + 1);
//n是字符串长度,m是关键字范围
//刚开始字符为关键字,122是'z'的ascii码
//之后以排名作为关键字,会改变
n = strlen(s + 1), m = 122;
SA();
return 0;
}
标签:lin 输入 数据 com nlogn 没有 ascii turn bit
原文地址:https://www.cnblogs.com/olinr/p/10158778.html