spoj 8222 Substrings (后缀自动机+dp)

题意:

给出一个串,定义这样的F(x){ 表示在长度为x的串中出现次数最多的串的长度 },求出F(i)  1<=i<=len

题解:

刚接触后缀自动机不会很懂,多亏大牛 在做这题前最好看下陈立杰的写的关于后缀数组的论文。论文中提到right(s),表示从s状态出发能到达的所有串的最后一个点的一个集合。len[i]表示在建后缀机的过程中某个过程对应的后缀长度,注意最后不一定会是整个串的后缀,但是可以将len[i]作为一个子串的长度来用。那么要得到s状态能到达的集合right,求出这个集合元素个数,这个个数就是s对应串出现的次数。我们根据right来根性max(s),采用dp来做,从大到小进行dp,这样就保证没有后效性,而且由于长度长的子串出现了某个次数,对应的更短子串也会出现同样次数,记得陈立杰论文提到right之间要么是包含关系要么不想交。由于spoj很慢,我们选择用桶排序(基数排序)。发现其实只要有状态的地方就可以dp,尤其是自动机的dp,越发感觉妙不可言。

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const int MOD=10007;
const int maxn=250005;
const int SIZE=maxn<<2;
const int type=26;
int next[SIZE][type],fa[SIZE],len[SIZE];
int tol,last;
int g[SIZE],dp[maxn],cnt[SIZE],pos[SIZE];
char str[maxn];

int newNode(int x){
    len[tol]=x;
    fa[tol]=-1;
    for(int i=0;i<type;i++){
        next[tol][i]=-1;
    }
    return tol++;
}

void Init(){
    tol=0;
    last=newNode(0);
}

void add(int k){

    int now=last;
    int end=newNode(len[now]+1);
    while(now!=-1&&next[now][k]==-1){
        next[now][k]=end;
        now=fa[now];
    }
    if(now==-1) fa[end]=0;
    else{
        int nxt=next[now][k];
        if(len[now]+1==len[nxt]) fa[end]=nxt;
        else{
            int cnxt=newNode(len[now]+1);
            for(int i=0;i<type;i++)next[cnxt][i]=next[nxt][i];
            fa[cnxt]=fa[nxt];
            fa[nxt]=fa[end]=cnxt;
            while(now!=-1&&next[now][k]==nxt){
                next[now][k]=cnxt;
                now=fa[now];
            }
        }
    }
    last=end;
}

void Insert(char buff[],int L){
    for(int i=0;i<L;i++)
        add(buff[i]-'a');
}


int main(){

    scanf("%s",str);
    int L=strlen(str);
    Init();
    Insert(str,L);
    for(int i=0;i<tol;i++)cnt[i]=0;
    for(int i=0;i<tol;i++)cnt[len[i]]++;
    for(int i=1;i<tol;i++)cnt[i]+=cnt[i-1];
    for(int i=tol-1;i>=0;i--)pos[--cnt[len[i]]]=i;
    for(int i=0;i<tol;i++)g[i]=0;
    for(int i=0;i<=L;i++)dp[i]=0;
    int p=0;
    for(int i=0;i<L;i++)g[p=next[p][str[i]-'a']]++;
    for(int i=tol-1;i>=0;i--){
        p=pos[i];
        dp[len[p]]=max(dp[len[p]],g[p]);
        g[fa[p]]+=g[p];
    }
    for(int i=L-1;i>=1;i--)dp[i]=max(dp[i],dp[i+1]);
    for(int i=1;i<=L;i++)printf("%d\n",dp[i]);
    return 0;
}




你可能感兴趣的:(spoj 8222 Substrings (后缀自动机+dp))