bzoj 5084: hashit

题意

你有一个字符串S,一开始为空串,要求支持两种操作
在S后面加入字母C
删除S最后一个字母
问每次操作后S有多少个不同的子串

前言

因为这题,我还倒回去复习了好一会的SAM。。
因为太久没有用过,都忘了。。

题解

很明显,题目给你的是一个字典树
然后你就可以dfs下去,然后顺便建立SAM
然后在回溯的时候将SAM还原。。
就可以 O ( n ) O(n) O(n)地解决这个问题了
upd:这样复杂度应该是有问题的。。。挖坑

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100005;
char ss[N];
int len;
int now,tot,last;
struct qq
{
    int son[26];
    int fa;
    int c;//他自己是什么字符 
}o[N];
void bt ()
{
    now=0;tot=0;
    for (int u=0;u<len;u++)
    {
        if (ss[u]=='-') now=o[now].fa;
        else
        {
            int x=ss[u]-'a';
            if (o[now].son[x]==0)   
            {
                o[now].son[x]=++tot;
                o[tot].c=x;
                o[tot].fa=now;
            }
            now=o[now].son[x];
        }
    }
}
struct qt{int son[26],step,pre;}s[N<<2];
int lalal=0;//现在有多少个不同的串了 
struct qy
{
    int x,y,z;
    int op;//什么类型 
}sta[N<<2];//撤销操作 
int num=0;//当前撤销到哪里 
void ins (int x)//插入这个新的点 
{
    int p=last,np=++tot;
    s[np].step=s[p].step+1;
    while (p!=0&&s[p].son[x]==0)    
    {
        /**/
        num++;
        sta[num].x=p;sta[num].y=x;sta[num].z=s[p].son[x];sta[num].op=0;
        /**/
        s[p].son[x]=np,p=s[p].pre;
    }
    if (p==0)   s[np].pre=1;
    else
    {
        int q=s[p].son[x];
        if (s[p].step+1==s[q].step) s[np].pre=q;
        else
        {
            int nq=++tot;
            s[nq]=s[q];
            s[nq].step=s[p].step+1;
            /**/
            num++;
            sta[num].x=q;sta[num].y=s[q].pre;sta[num].op=1;
            /**/
            s[q].pre=s[np].pre=nq;
            while (p!=0&&s[p].son[x]==q) 
            {
                /**/
                num++;
                sta[num].x=p;sta[num].y=x;sta[num].z=s[p].son[x];sta[num].op=0;
                /**/
                s[p].son[x]=nq;
                p=s[p].pre;
            }
        }
    }
    lalal=lalal+s[np].step-s[s[np].pre].step;
    last=np;
}
int ans[N];//每个节点的答案 
void dfs (int x)
{
    int now=num,shen=lalal,ooo=last;
    ins(o[x].c);
    ans[x]=lalal;
    for (int u=0;u<26;u++)
        if (o[x].son[u]!=0)
            dfs(o[x].son[u]);
    for (int u=now+1;u<=num;u++)//撤销操作
    {
        int x=sta[u].x,y=sta[u].y,z=sta[u].z;
        if (sta[u].op==0)//son的修改
            s[x].son[y]=z;
        if (sta[u].op==1)//pre的修改
            s[x].pre=y;
    }
    lalal=shen;last=ooo;num=now;
}
void solve ()
{
    tot=1;
    for (int u=0;u<26;u++)
        if (o[0].son[u]!=0)
        {
            s[1].step=0;s[1].pre=0;
            for (int i=0;i<26;i++) s[1].son[i]=0;
            last=1;lalal=0;
            dfs(o[0].son[u]);
        }
    now=0;
    for (int u=0;u<len;u++)
    {
        if (ss[u]=='-') now=o[now].fa;
        else
        {
            int x=ss[u]-'a';
            now=o[now].son[x];
        }
        printf("%d\n",ans[now]);
    }
}
int main()
{
    scanf("%s",ss);len=strlen(ss);
    bt();
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值