后缀数组裸题,求出一个字符串所有子串个数后,减去height的和就OK。
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <iostream>
#include <assert.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int M = 1e4 + 10;
int w[M];
int rankk[M * 2];
int sa[M];
int height[M];
char s[M];
void get_sa_hei(int len)
{
static int m[M], tmp[M];
//int len = strlen(s + 1);
memset(rankk, 0, sizeof(int) * len * 2);
for (int i = 0; i <= 255; i++) w[i] = 0;
for (int i = 1; i <= len; i++) w[s[i]]++;
for (int i = 1; i <= 255; i++) w[i] += w[i - 1];
for (int i = len; i; i--) tmp[w[s[i]]--] = i;
rankk[tmp[1]] = 1;
for (int i = 2; i <= len; i++)
rankk[tmp[i]] = rankk[tmp[i-1]] + (s[tmp[i]] != s[tmp[i-1]]);
//get sa 用基数排序
for (int k = 1; k <= len; k <<= 1) {
for (int i = 0; i <= len; i++) w[i] = 0;
for (int i = 1; i <= len; i++) w[rankk[i+k]]++;
for (int i = 1; i <= len; i++) w[i] += w[i-1];
for (int i = len; i; i--) m[w[rankk[i+k]]--] = i;
for (int i = 0; i <= len; i++) w[i] = 0;
for (int i = 1; i <= len; i++) w[rankk[i]]++;
for (int i = 1; i <= len; i++) w[i] += w[i-1];
for (int i = len; i; i--) tmp[w[rankk[m[i]]]--] = m[i];
m[tmp[1]] = 1;
for (int i = 2; i <= len; i++)
m[tmp[i]] = m[tmp[i-1]] + (rankk[tmp[i]] != rankk[tmp[i-1]] || rankk[tmp[i]+k] != rankk[tmp[i-1]+k]);
for (int i = 1; i <= len; i++) rankk[i] = m[i];
}
for (int i = 1; i <= len; i++) sa[rankk[i]] = i;
//get height
for (int i = 1, j = 0; i <= len; i++) {
if (rankk[i] == 1) continue;
for (j ? j-- : 0; s[sa[rankk[i]-1]+j] == s[i+j]; j++);
height[rankk[i]] = j;
}
}
int main()
{
int cascnt;
cin >> cascnt;
while (cascnt--) {
scanf("%s", s + 1);
int len = strlen(s + 1);
get_sa_hei(len);
int ans = len * (len + 1) / 2;
for (int i = 1; i <= len; i++) {
ans -= height[i];
}
printf("%d\n", ans);
}
return 0;
}