bzoj 5084 hashit

http://www.elijahqi.win/archives/3834
Description
你有一个字符串S,一开始为空串,要求支持两种操作
在S后面加入字母C
删除S最后一个字母
问每次操作后S有多少个两两不同的连续子串

Input
一行一个字符串Q,表示对S的操作
如果第i个字母是小写字母c,表示第一种加字母c的操作
如果为-表示删除操作,保证所有删除操作前S都非空
|Q|<=10^5

Output
输出|Q|行,第i行表示i个操作之后S内有多少个不同子串

Sample Input
aba-caba
Sample Output
1
3
5
3
6
9
12
17
HINT

Source
By clj

一开始复杂度没想清楚 yy一下开始写 还拿了bzoj rk1

然而discuss里的数据直接让我跑到24s qwq

正解待填坑 蒟蒻我的做法就是暴力回退 发现每回只删除一个 那么只删除一个 在sam上是可以直接暴力 那么删除多个的情况我们一个一个暴力即可

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char s[N];int ch[N<<1][26],len[N<<1],fa[N<<1],cnt=1,root=1,last=1;
int pd[N<<3],top,top1,lst[N<<1],c[N<<1],ans,ed[N<<1];
inline void insert1(int x){
    ed[cnt+1]=0;int p=last,np=++cnt;len[np]=len[p]+1;c[cnt]=x;
    for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;pd[++top]=p;lst[++top1]=last;
    if (!p) fa[np]=root;else{
        int q=ch[p][x];
        if (len[p]+1==len[q]) fa[np]=q;else{
            int nq=++cnt;memcpy(ch[nq],ch[q],sizeof(ch[q]));ed[cnt]=2;
            pd[++top]=fa[q];fa[nq]=fa[q];fa[q]=fa[np]=nq;len[nq]=len[p]+1;
            for (;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
            pd[++top]=p;pd[++top]=q;c[cnt]=x;
        }
    }last=np;ans+=len[np]-len[fa[np]];if (ed[cnt]!=2) ed[cnt]=1;
}
inline void gao(){
    if(ed[cnt]==2){
        int q=pd[top--],tp1=pd[top--],fq=pd[top--],tp2=pd[top--];
        int p=lst[top1--],x=c[cnt];last=p;
        ans-=len[cnt-1]-len[fa[cnt-1]];
        while(p!=tp2) {ch[p][x]=0;p=fa[p];}
        fa[q]=fq;while(p!=tp1) ch[p][x]=q,p=fa[p];
        memset(ch[cnt],0,sizeof(ch[cnt]));--cnt;
        memset(ch[cnt],0,sizeof(ch[cnt]));--cnt;
    }else{
        int p=lst[top1--],tp=pd[top--],x=c[cnt];
        ans-=len[cnt];ans+=len[fa[cnt]];last=p;
        while(p!=tp) ch[p][x]=0,p=fa[p];
        memset(ch[cnt],0,sizeof(ch[cnt]));--cnt;
    }
}
int main(){
    freopen("bzoj5084.in","r",stdin);
    //freopen("bzoj5084.out","w",stdout);
    //double t1=clock();
    scanf("%s",s+1);
    for (int i=1;s[i];++i){
        if (s[i]<'a'||s[i]>'z') gao();
        else insert1(s[i]-'a');
        printf("%d\n",ans);
    }
    //double t2=clock();
    //printf("%f\n",(t2-t1)/CLOCKS_PER_SEC);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值