本文主要是介绍luogu 2408 不同子串个数 (后缀数组),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
题目背景
因为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);
}
这篇关于luogu 2408 不同子串个数 (后缀数组)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!