【SDOI2016】生成魔咒

Description

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。
一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒。
例如 S=[1,2,1] 时,它的生成魔咒有 [1]、[2]、[1,2]、[2,1]、[1,2,1] 五种。S=[1,1,1] 时,它的生成魔咒有 [1]、[1,1]、[1,1,1] 三种。
最初 S 为空串。共进行 n 次操作,每次操作是在 S 的结尾加入一个魔咒字符。每次操作后都需要求出,当前的魔咒串 S 共有多少种生成魔咒。

Solution

后缀自动机做又短又快

后缀自动机学习小记
每一次 ans+=t[np].lent[t[np].fa].len 就可以了。

直接减trie空间不行

c++有一个map,简单的代替trie的数组。
不然,可以hash然后建邻接表。

Code

#include 
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100007;
int i,j,k,l,n,m,last,num,yi,er;
long long ans;
struct node{
    int len,fa;
}t[maxn*2];
map<int,int>son[maxn*2];
int np,nq,p,q;
void extend(int c){
    p=last,np=++num;
    t[np].len=t[p].len+1;
    while(p&&!son[p][c])son[p][c]=np,p=t[p].fa;
    if(!p)t[np].fa=1;
    else{
        q=son[p][c];
        if(t[q].len==t[p].len+1)t[np].fa=q;
        else{
            nq=++num;
            for(map<int,int>::iterator i=son[q].begin();i!=son[q].end();i++){
                son[nq][i->first]=i->second;    
            }
            t[nq]=t[q];
            t[nq].len=t[p].len+1;
            t[q].fa=t[np].fa=nq;
            while(p&&son[p][c]==q)son[p][c]=nq,p=t[p].fa;
        }
    }
    last=np;
}
int main(){
    freopen("incantation.in","r",stdin);
    freopen("incantation.out","w",stdout);
    scanf("%d",&n);
    last=num=1;
    fo(i,1,n){
        scanf("%d",&k);
        extend(k);
        ans+=t[np].len-t[t[np].fa].len;
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(省选,后缀自动机,SDOI,生成魔咒,后缀自动机,不同子串的个数,SAM)