题目背景
因为NOI被虐傻了,蒟蒻的YJQ准备来学习一下字符串,于是它碰到了这样一道题:
题目描述
给你一个长为N的字符串,求不同的子串的个数
我们定义两个子串不同,当且仅当有这两个子串长度不一样 或者长度一样且有任意一位不一样。
子串的定义:原字符串中连续的一段字符组成的字符串
输入格式
第一行一个整数N
接下来一行N个字符表示给出的字符串
输出格式
一行一个整数,表示不一样的子串个数
输入输出样例
输入 #1
5
aabaa
输出 #1
11
输入 #2
3
aba
输出 #2
5
说明/提示
请使用64位整数来进行输出
(具体来说,C++和C选手请使用long long 类型,pascal选手请使用Int64)
由于输入文件过大,请使用 高效的读入方法(具体的,c++和c选手请不要使用cin,pascal选手不需要管)
对于30%的数据,N\le 1000N≤1000
对于100%的数据,N\le 10^5N≤105
题目链接:https://www.luogu.org/problem/P2408
题目分析:求出height,枚举排名,对排名为i的后缀,长度为n - sa[i] + 1,其中存在的不同的子串个数需要减去公共前缀,即h[i]
所以答案为∑(n - sa[i] + 1 - h[i])
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
int const MAX = 1e5 + 5;
char s[MAX];
int n, m, sa[MAX], height[MAX];
int rk[MAX], tp[MAX], tax[MAX];
bool cmp(int* r, int a, int b, int k) {
return r[a] == r[b] && r[a + k] == r[b + k];
}
void radix_sort() {
for (int i = 0; i <= m; i++) {
tax[i] = 0;
}
for (int i = 1; i <= n; i++) {
tax[rk[tp[i]]]++;
}
for (int i = 1; i <= m; i++) {
tax[i] += tax[i - 1];
}
for (int i = n; i >= 1; i--) {
sa[tax[rk[tp[i]]]--] = tp[i];
}
}
void get_sa() {
for (int i = 1; i <= n; i++) {
rk[i] = s[i] - 'a' + 1;
tp[i] = i;
m = max(m, s[i] - 'a' + 1);
}
radix_sort();
for (int j = 1, p = 0; p < n; j <<= 1, m = p) {
p = 0;
for (int i = n - j + 1; i <= n; i++) {
tp[++p] = i;
}
for (int i = 1; i <= n; i++) {
if (sa[i] > j) {
tp[++p] = sa[i] - j;
}
}
radix_sort();
swap(tp, rk);
rk[sa[1]] = p = 1;
for (int i = 2; i <= n; i++) {
rk[sa[i]] = cmp(tp, sa[i], sa[i - 1], j) ? p : ++p;
}
}
}
void get_height() {
for (int i = 1, j = 0; i <= n; i++) {
if (j) {
j--;
}
int prevPos = sa[rk[i] - 1];
while (i + j <= n && prevPos + j <= n && s[i + j] == s[prevPos + j]) {
j++;
}
height[rk[i]] = j;
}
}
int main() {
scanf("%d %s", &n, s + 1);
get_sa();
get_height();
// for (int i = 1; i <= n; i++) {
// printf("sa[%d] = %d rk[%d] = %d height[%d] = %d\n", i, sa[i], i, rk[i], i, height[i]);
// }
ll ans = 0;
for (int i = 1; i <= n; i++) {
int suffixLen = n - sa[i] + 1;
ans += (ll) (suffixLen - height[i]);
}
printf("%lld\n", ans);
}