后缀自动机三·重复旋律6
时间限制:
15000ms
单点时限:
3000ms
内存限制:
512MB
-
aab
样例输出
-
2 1 1
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。
现在小Hi想知道一部作品中所有长度为K的旋律中出现次数最多的旋律的出现次数。但是K不是固定的,小Hi想知道对于所有的K的答案。
输入
共一行,包含一个由小写字母构成的字符串S。字符串长度不超过 1000000。
输出
共Length(S)行,每行一个整数,表示答案。
Source
My Solution
题意:求一个字符串中,所有长度为K的子串出现次数最多的子串的出现次数,但是K不是固定的,求所有的K的答案。
后缀自动机、递推、DFS
首先 ans[MAXN] 必定是单调不增序列,
一个子串出现的次数是它的endpos集合的大小,由于endpos[u]的集合为以u代表的子树的endpos集合的并集,可以跑一边dfs,预处理出所有的endpossz[i]。
所以只需要根据pre树求出,ans[maxlen(st)] = max(ans[maxlen[st], endpossz[st]),然后从len~0递推,ans[i] = max(ans[i], ans[i+1])。
复杂度 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], endpossz[MAXN];
int last, tot;
void init(){
last = tot = 0;
memset(ch[0], -1, sizeof ch[0]);
pre[0] = -1; val[0] = 0;
}
void 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;
}
vector<int> sons[2*MAXN];
void build_the_tree(){
for(int i = 1; i <= tot; i++){
sons[pre[i]].push_back(i);
}
}
void get_endpos_sz(int u){
int sz = sons[u].size(), i;
if(endpos[u] > 0) endpossz[u] = 1;
else endpossz[u] = 0;
for(i = 0; i < sz; i++){
get_endpos_sz(sons[u][i]);
endpossz[u] += endpossz[sons[u][i]];
}
}
int ans[MAXN];
void run(int n){
memset(ans, 0, sizeof ans);
for(int i = 1; i <= tot; i++){
ans[val[i]] = max(ans[val[i]], endpossz[i]);
}
for(int i = n - 1; i >= 1; i--){
ans[i] = max(ans[i], ans[i+1]); //从后往前维护
}
}
} sam;
int main()
{
#ifdef LOCAL
freopen("22.in", "r", stdin);
//freopen("22.out", "w", stdout);
int T = 2;
while(T--){
#endif // LOCAL
ios::sync_with_stdio(false); cin.tie(0);
int n, i;
cin >> s;
n = s.size();
sam.init();
for(i = 0; i < n; i++) sam.extend(s[i] - 'a', i+1);
sam.build_the_tree();
sam.get_endpos_sz(0);
sam.run(n);
for(i = 1; i <= n; i++){
cout << sam.ans[i] << "\n";
}
#ifdef LOCAL
cout << endl;
}
#endif // LOCAL
return 0;
}
Thank you!
------from ProLights