题解:P10469 后缀数组
思路
求取
这里用到的技巧是:比较两个字符串的字典序,可以通过哈希 + 二分的优化达到单次询问
-
从第一个字符开始比较。
-
如果当前字符不相等,就直接比较这两个字符,即可得出答案。
-
否则,继续比较下一个字符。
这个过程实际上就是在寻找两个字符串的最长公共前缀,而下一个字符就一定不相等。两个字符串的最长公共前缀如何求取?同 P10479 匹配统计,这是具有单调性的(事实上,你可以在 P10479 里看到我的题解,与下面的描述一样):如果前
而
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef unsigned long long ull;
const int N = 3e5 + 5, base = 1e9 + 7;
string s;
int n, SA[N], Height[N];
ull hs[N], pw[N];
ull get_hash(int l, int r) {
return hs[r] - hs[l - 1] * pw[r - l + 1];
}
int find_pos(int x, int y) {
int l = -1, r = min(n - x + 1, n - y + 1) + 1;
while (l + 1 < r) {
int mid = l + r >> 1;
if (get_hash(x, x + mid - 1) == get_hash(y, y + mid - 1))
l = mid;
else
r = mid;
}
return l;
}
bool cmp(int x, int y) {
int pos = find_pos(x, y);
return s[x + pos] < s[y + pos];
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> s;
n = s.size();
s = '#' + s;
pw[0] = 1;
for (int i = 1; i <= n; i++) {
SA[i] = i;
pw[i] = pw[i - 1] * base;
hs[i] = hs[i - 1] * base + s[i];
}
sort(SA + 1, SA + 1 + n, cmp);
for (int i = 1; i <= n; i++)
cout << SA[i] - 1 << " "; // 注意题目中的下标从0开始
cout << "\n";
for (int i = 1; i <= n; i++)
cout << find_pos(SA[i], SA[i - 1]) << " ";
return 0;
}