首先说一下问题描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。
现在小Hi想知道一部作品中所有长度为K的旋律中出现次数最多的旋律的出现次数。但是K不是固定的,小Hi想知道对于所有的K的答案。
输入
共一行,包含一个由小写字母构成的字符串S。字符串长度不超过 1000000。
输出共Length(S)行,每行一个整数,表示答案。
样例输入
aab
样例输出
2
1
1
状态 | 字符串 | endpos |
S | 空串 | {0,1,2,3,4,5,6} |
1 | a | {1,2,5} |
2 | aa | {2} |
3 | aab | {3} |
4 | aabb,abb,bb | {4} |
5 | b | {3,4,6} |
6 | aabba,abba,bba,ba | {5} |
7 | aabbab,abbab,bbab,bab | {6} |
8 | ab | {3,6} |
9 | aabbabd,abbabd,bbabd,babd,abd,bd,d | {7} |
先构建SAM,顺便将主干点标示出来,然后对slink构成的树自底向上用拓扑排序构件出来|endpos(st)|
值得注意的是ans[1], ans[2], ... ans[length(S)]一定是一个单调递减序列。所以我们对于每个状态st,只需要更新ans[maxlen(st)]。之后令i = length(S)-1 .. 1,从后向前扫描一遍,令ans[i] = max(ans[i], ans[i+1]),即可。
代码:
//
// main.cpp
// hiho129
//
// Created by 小哲 on 16/12/17.
// Copyright © 2016年 小哲. All rights reserved.
//
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
const int MAXL = 1000000;
string s;
int n = 0;
int maxlen[2 * MAXL + 10], minlen[2 * MAXL + 10], trans[2 * MAXL + 10][26], slink[2 * MAXL + 10],lv[2*MAXL],ans[MAXL],endpos[2*MAXL];
int du[2*MAXL];
int new_state(int _maxlen, int _minlen, int* _trans, int _slink) {
maxlen[n] = _maxlen;
minlen[n] = _minlen;
for(int i = 0; i < 26; i++) {
if(_trans == NULL)
trans[n][i] = -1;
else
trans[n][i] = _trans[i];
}
slink[n] = _slink;
lv[n]=0;
endpos[n]=0;
du[n]=0;
return n++;
}
int add_char(char ch, int u) {
int c = ch - 'a';
int z = new_state(maxlen[u] + 1, -1, NULL, -1);
lv[z]=1;
int v = u;
while(v != -1 && trans[v][c] == -1) {
trans[v][c] = z;
v = slink[v];
}
if(v == -1) { //最简单的情况,suffix-path(u->S)上都没有对应字符ch的转移
minlen[z] = 1;
slink[z] = 0;
return z;
}
int x = trans[v][c];
if(maxlen[v] + 1 == maxlen[x]) { //较简单的情况,不用拆分x
minlen[z] = maxlen[x] + 1;
slink[z] = x;
return z;
}
int y = new_state(maxlen[v] + 1, -1, trans[x], slink[x]); //最复杂的情况,拆分x
slink[y] = slink[x];
minlen[x] = maxlen[y] + 1;
slink[x] = y;
minlen[z] = maxlen[y] + 1;
slink[z] = y;
int w = v;
while(w != -1 && trans[w][c] == x) {
trans[w][c] = y;
w = slink[w];
}
minlen[y] = maxlen[slink[y]] + 1;
return z;
}
void getEndpos()// 拓扑排序
{
for (int i=1;i<n ; i++) {
du[slink[i]]++;
}
queue<int> qu;
for (int i=1; i<n; i++) {
if (!du[i]) {
qu.push(i);
}
}
while (!qu.empty()) {
int v = qu.front(); // 从队列中取出一个顶点
qu.pop();
if (lv[v]) {
endpos[v]++;
}
endpos[slink[v]]+=endpos[v];
du[slink[v]]--;
if (!du[slink[v]]) {
qu.push(slink[v]);
}
}
}
void getAns(){//扫描获取值
for (int i=0; i<=s.length(); i++) {
ans[i]=0;
}
for (int i=0; i<n; i++) {
ans[maxlen[i]]=max(ans[maxlen[i]], endpos[i]);
}
for (int i=s.length()-1; i>0; i--) {
ans[i]=max(ans[i],ans[i+1]);
}
}
int main(int argc, const char * argv[]) {
int nextI=new_state(0, -1, NULL, -1);;
cin>>s;
for (int i=0; i<s.length(); i++) {
nextI= add_char(s[i],nextI);
}
getEndpos();
getAns();
for (int i=1; i<=s.length(); i++) {
cout<<ans[i]<<endl;
}
return 0;
}