代码改变世界

codeforces 432D Prefixes and Suffixes

2014-08-01 18:17  bootstar  阅读(444)  评论(0编辑  收藏  举报

由于包含了前缀与后缀,很容易想到用KMP去算前缀与后缀的公共缀。另外要计算某个后缀在整个串中出现的次数,由于后缀自动机是比较容易求的,然后就直接上后缀自动机了。先分别用KMP算法与后缀自动机跑一遍,然后对后缀自动机做一个拓扑排序,根据拓扑结构更新一遍每个串出现的次数就可以了。然后直接沿着KMP的next数组以及后缀自动机节点的父亲边走就可以了。

代码如下:

#include <stdio.h>
#include <string.h>
const int maxn = 100100;

struct sanode{
    sanode *f, *ch[26];
    int l, m;
    sanode(){
        f = 0;
        memset(ch, 0, sizeof(ch));
        l = 0, m = 1;
    }
    void init(){
        f = 0;
        memset(ch, 0, sizeof(ch));
        l = 0, m = 1;
    }    
}sam[maxn*2], *b[maxn*2];
int cnt[maxn], tot;
sanode *tail, *s;
char str[maxn];
int f[maxn]; //KMP
struct resultSet{
    int l, m;
}res[maxn];

void addSuffix(int c, int len){
    sanode *p = tail, *np = &sam[++tot];
    np->init();
    tail = np;
    np->l = len;

    for(;p&&!p->ch[c];p=p->f) p->ch[c] = np;
    if(!p) np->f = s;
    else{
        if(p->ch[c]->l == p->l + 1) np->f = p->ch[c];
        else{
            sanode * q = p->ch[c], *r = &sam[++tot];
            *r = *q;
            r->l = p->l + 1;
            r->m = 0;
            q->f = np->f = r;
            for(;p && p->ch[c]==q; p=p->f) p->ch[c] = r;
        }
    }
}

void topSortSuffix(int len){
    for(int i = 0; i <= tot; i ++) cnt[sam[i].l] ++;
    for(int i = 1; i <= len; i ++) cnt[i] += cnt[i-1];
    for(int i = 0; i <= tot; i ++) b[--cnt[sam[i].l]] = &sam[i];

    for(int i = tot; i > 0; i --){
        b[i]->f->m += b[i]->m;
    }
}
void KMP(){
    int c = 0;
    for(int i = 2; str[i]; i ++){
        while(c && str[c+1] != str[i])
            c = f[c];
        if(str[c+1] == str[i])
            f[i] = ++c;
    }
}

int main(){
    int len = 0;
    s = tail = &sam[tot = 0];
    s->init();
    scanf("%s", str+1);
    for(len = 1; str[len]; len ++){
        addSuffix(str[len] - 'A', len);
    }
    len --;
    topSortSuffix(len);
    KMP();
    sanode *p = tail;
    int number = 0;
    for(; len; len = f[len]){
        while(p && p->l > len)
            p = p->f;
        if(!p) break;
        if(p->l == len){
            res[number].l = len;
            res[number].m = p->m;
            number ++;
        }
    }
    printf("%d\n", number);
    for(int i = number - 1; i >= 0; i --)
        printf("%d %d\n", res[i].l , res[i].m);
    
    return 0;
}