后缀自动机二·重复旋律5
时间限制:
10000ms
单点时限:
2000ms
内存限制:
512MB
-
aab
样例输出
-
5
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。
现在小Hi想知道一部作品中出现了多少不同的旋律?
输入
共一行,包含一个由小写字母构成的字符串。字符串长度不超过 1000000。
输出
一行一个整数,表示答案。
Source
My Solution
题意:统计字符串中不同子串的个数。
后缀自动机
这题是后缀自动机基础题,直接使用SAM上的pre树, sigma{val[np] - val[pre[np]]}
复杂度 O(n)
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
using namespace std;
typedef long long LL;
const int MAXN = 2*1e6 + 8;
string s;
struct SAM{
int ch[MAXN][26], pre[MAXN], val[MAXN], endpos[MAXN];
int last, tot;
void init(){
last = tot = 0;
memset(ch[0], -1, sizeof ch[0]);
pre[0] = -1; val[0] = 0;
}
int extend(int c, int ind){
int p = last, np = ++tot;
val[np] = val[p] + 1; endpos[np] = ind;
memset(ch[np], -1, sizeof ch[np]);
while(~p && ch[p][c] == -1) ch[p][c] = np, p = pre[p];
if(p == -1) pre[np] = 0;
else{
int q = ch[p][c];
if(val[q] != val[p] + 1){
int nq = ++tot;
memcpy(ch[nq], ch[q], sizeof ch[q]);
val[nq] = val[p] + 1;
pre[nq] = pre[q];
pre[q] = pre[np] = nq;
while(~p && ch[p][c] == q) ch[p][c] = nq, p = pre[p];
}
else pre[np] = q;
}
last = np;
return val[np] - val[pre[np]];
}
/*
vector<int> sons[2*MAXN];
void build_the_tree(){
for(int i = 1; i <= tot; i++){
sons[pre[i]].push_back(i);
}
}
*/
} sam;
int main()
{
#ifdef LOCAL
freopen("21.in", "r", stdin);
//freopen("21.out", "w", stdout);
int T = 2;
while(T--){
#endif // LOCAL
ios::sync_with_stdio(false); cin.tie(0);
int n, i;
LL sum =0;
cin >> s;
n = s.size();
sam.init();
for(i = 0; i < n; i++) sum += sam.extend(s[i] - 'a', i+1);
cout << sum << endl;;
#ifdef LOCAL
cout << endl;
}
#endif // LOCAL
return 0;
}
Thank you
------from ProLights