BZOJ4516 [Sdoi2016]生成魔咒

题意:

一个初始为空的串,每次在末尾插入一个字符,每次插入后问字符串中本质不同的子串的个数。字符值域1e9。

知识点:

后缀自动机

解法:

因为SAM本来就是支持动态末尾插入的在线结构,而且SAM有一个性质是本质不同子串个数等于所有点的len减去parent树上的父亲点的len,所以每次插入改ans即可。

备注:

因为值域很大,所以改用map维护转移的字符。
另外好像后缀数组也可以做,加点什么平衡树也可以做,不过我这种方法应该代码最好写、最短(里面有一些map的find、insert操作可以在访问空节点的时候变快)。

#include<cstdio>
#include<cstring>
#include<map>
using namespace std;

typedef long long ll;
const int maxn=200010;
ll ans;
int n,m,tot,lst;
struct sam
{
    map<int,int>son;
    int fa,len;
}a[maxn];

int read()
{
    int x=0;
    char c=getchar();
    while (c<48||c>57)
        c=getchar();
    while (c>=48&&c<=57)
        x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}

void sam_insert(int c)
{
    int p=lst,u=(++tot);
    lst=u;
    a[u].len=a[p].len+1;
    for (;p&&a[p].son.find(c)==a[p].son.end();p=a[p].fa)
        a[p].son.insert(make_pair(c,u));
    if (!p)
        a[u].fa=1;
    else
    {
        int v=a[p].son[c];
        if (a[v].len==a[p].len+1)
            a[u].fa=v;
        else
        {
            int w=(++tot);
            a[w]=a[v];
            a[w].len=a[p].len+1;
            a[v].fa=a[u].fa=w;
            for (;p&&a[p].son[c]==v;p=a[p].fa)
                a[p].son[c]=w;
        }
    }
    ans+=a[u].len-a[a[u].fa].len;
}

int main()
{
    int x;
    tot=lst=1;
    n=read();
    while (n--)
    {
        x=read();
        sam_insert(x);
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Ronald-MOK1426/p/12294374.html