1. 程式人生 > >@spoj - [email protected] Substrings

@spoj - [email protected] Substrings

目錄


@[email protected]

給定一個僅包含小寫字母的字串 S,對於每一個 i 滿足 1 <= i <= |S|,求長度為 i 的,在 S 中出現次數最多的串出現了多少次?

input
輸入一個長度小於等於 250000 的,僅包含小寫字母的串。

output
輸出 |S| 行,第 i 行表示長度為 i 的在 S 中出現次數最多的串的出現次數。

sample input
ababa
sample output
3
2
2
1
1

@[email protected]

想想我們在後綴自動機中 end-pos 的含義:每一次出現的結束位置的集合。
我們要求解它出現了多少次,即要求解 |end-pos|。

再想想我們構建字尾自動機採用的是增量法。
每次加入一個字元,就會多出來一個以前從未出現過的一個新結束位置。

然後想想我們 fa 的含義,一個結點的 end-pos 必然是 fa 的 end-pos 的子集。
所以假如多出來一個新的結束位置,那麼它會影響它所有的祖先。

最後,一個結點的最長子串長度必然大於它 fa 的最長子串,因此我們可以按照最長子串長度進行桶排序,再從後往前掃一邊,更新父親的值,就可以求解出每一個結點的出現次數。

假如某一個結點出現次數為 k,那麼它的最長子串的所有後綴出現次數也一定大於等於 k,所以我們可以直接用 k 去更新最長子串長度的值,再從後往前用後一個去更新前一個。

@accepted [email protected]

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 250000;
struct sam{
    sam *ch[26], *fa; int mx;
    sam *nxt; int siz;
}pl[2*MAXN + 5], *bin[MAXN + 5], *tcnt, *root, *lst;
void init() {
    tcnt = root = &pl[0];
    for(int i=0;i<26;i++)
        root->ch[i] = NULL;
    root->fa = NULL, root->mx = 0;
}
void add_bin(sam *x) {
    x->nxt = bin[x->mx];
    bin[x->mx] = x;
}
sam *newnode() {
    tcnt++;
    for(int i=0;i<26;i++)
        tcnt->ch[i] = NULL;
    tcnt->fa = NULL, tcnt->mx = 0;
    return tcnt;
}
void sam_extend(int x) {
    sam *cur = newnode(), *p = lst;
    cur->mx = lst->mx + 1, cur->siz = 1, lst = cur;
    add_bin(cur);
    while( p && !p->ch[x] )
        p->ch[x] = cur, p = p->fa;
    if( !p )
        cur->fa = root;
    else {
        sam *q = p->ch[x];
        if( q->mx == p->mx + 1 )
            cur->fa = q;
        else {
            sam *cne = newnode();
            (*cne) = (*q), cne->mx = p->mx + 1, cne->siz = 0;
            add_bin(cne);
            q->fa = cur->fa = cne;
            while( p && p->ch[x] == q )
                p->ch[x] = cne, p = p->fa;
        }
    }
}
char s[MAXN + 5]; int ans[MAXN + 5];
int main() {
    init(); lst = root;
    scanf("%s", s); int lens = strlen(s);
    for(int i=0;i<lens;i++)
        sam_extend(s[i] - 'a');
    for(int i=lens;i>=1;i--) {
        while( bin[i] ) {
            bin[i]->fa->siz += bin[i]->siz;
            ans[i] = max(ans[i], bin[i]->siz);
            bin[i] = bin[i]->nxt;
        }
    }
    for(int i=1;i<=lens;i++)
        printf("%d\n", ans[i]);
}

@[email protected]

我們的 end-pos 大小並不等於子樹大小,而是等於子樹內非複製的(即每一次用增量法加入的)點的個數。