【后缀自动机】[HDU 4641]K-string

题目大意:多个询问,给出一个原始的字符串,多次操作每次要么在原串的末尾加入一个新的字符,要么询问当前的字符串中有多少个子串重复出现了至少k次

实际上和大部分后缀自动机的题目差不多,根据后缀自动机的原理建完树之后实际上每一个节点到root的最路径的所有种数实际上就是每一个子串那么在每一个节点开一个number表示当前这个子串被匹配的次数,然后pre一定是当前子串的子串那么number[pre] += number[当前]如果已经大于等于了k那么就没有必要加了,如果加了1之后等于了k那么说明当前串为结尾的已经可以被匹配了,同时发现在当前串和pre[当前串]所表示的两个子串之间的所有子串,实际上都可以被匹配了,那么实际上就是ans+=len[当前]-len[pre[当前]] 为什么一定是这样的呢,因为每一次新加入一个节点那么当前表示的子串之前一定没有出现过。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 200000;
int ans, k;
struct node{
    node *p[26], *pre;
    int len, number;
}Edges[MAXN*3+10], *ecnt=Edges+1,*root=Edges,*last=Edges;
node *p, *np;
void Insert(int w){
    p = last; last = np = ecnt++; 
    np->len = p->len+1; np->number = 0;
    memset(np->p, 0, sizeof np->p);
    while(p&&p->p[w]==NULL) p->p[w]=np, p=p->pre;
    if(!p) np->pre = root;
    else{
        node *q = p->p[w];
        if(p->len+1 == q->len){
            np->pre = q;
        }else{
            node *nnd = ecnt++;
            *nnd = *q;
            nnd->len = p->len+1;
            q->pre = np->pre = nnd;
            while(p&&p->p[w]==q)
                p->p[w]=nnd, p=p->pre;
        }
    }
    while(np!=root && np->number < k){
        (np->number)++;
        if(np->number == k){
            ans+=np->len-np->pre->len;
            break;
        }
        np = np->pre;
    }
}
char tm[500010];
int main(){
    int n, m, ord; char rd;
    while(~scanf("%d%d%d", &n, &m, &k)){
        ans = 0;
        root = Edges, last = Edges;
        ecnt = Edges+1; memset(root->p, 0, sizeof root->p);
        root->number =0; root->pre = NULL;
        scanf("%s", tm);
        for(int i=0;i<n;i++)
            Insert(tm[i]-'a');
        while(m--){
            scanf("%d", &ord);
            if(ord == 1){
                getchar(); rd=getchar();
                Insert(rd-'a');
            }else printf("%d\n", ans);
        }
    }

    return 0;
}

posted on 2015-06-17 13:36  JeremyGuo  阅读(315)  评论(0编辑  收藏  举报

导航