HDU 4641 至少出现K次本质不同子串数:后缀自动机

题意:先给出一个串,然后有若干操作。操作1:在结尾续上一个新字符。操作2:查询至少出现了K次的,本质不同的子串个数。


题解:SAM裸题,插入一个新的字符之后,就暴力在parent上转移++,但是也不能那么暴力,我们知道parent链上的num是单调增的,当遇到一个num>=k的点就不需要再继续走了,因为前边的肯定统计到答案里边了。


Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 25e4+1000;
char s[maxn];
int len,k,n,m;
char temp[5];
struct SAM{
	int last,cnt,nxt[maxn*2][26],fa[maxn*2],l[maxn*2],num[maxn*2];
	int ans;
	void init(){
		last = cnt=1;
		memset(nxt[1],0,sizeof nxt[1]);
		fa[1]=l[1]=num[1]=0;
		ans=0;
	}
	int inline newnode(){
		cnt++;
		memset(nxt[cnt],0,sizeof nxt[cnt]);
		fa[cnt]=l[cnt]=num[cnt]=0;
		return cnt;
	}
	void add(int c){
		int p = last;  
        int np = newnode();  
        last = np;  
        l[np] =l[p]+1;  
        while (p&&!nxt[p][c]){  
            nxt[p][c] = np;  
            p = fa[p];  
        }  
        if (!p){  
            fa[np] =1;  
        }else{  
            int q = nxt[p][c];  
            if (l[q]==l[p]+1){  
                fa[np] =q;  
            }else{  
                int nq = newnode();  
                memcpy(nxt[nq],nxt[q],sizeof nxt[q]);  
                fa[nq] =fa[q];  
                num[nq] = num[q];
                l[nq] = l[p]+1;  
                fa[np] =fa[q] =nq;  
                while (nxt[p][c]==q){  
                    nxt[p][c]=nq;  
                    p = fa[p];  
                }  
            }  
        }
		int temp = last;
		while (temp){
			if (num[temp]>=k){
				break;
			}
			num[temp]++;
			if (num[temp]==k){
				ans+=l[temp]-l[fa[temp]];
			}
			temp = fa[temp];
		}  
	}
}sam;
int main(){
	while (scanf("%d%d%d",&n,&m,&k)!=EOF){
		scanf("%s",s);
		len = strlen(s);
		sam.init();
		for (int i=0;i<len;i++){
			sam.add(s[i]-'a');
		}
		while (m--){
			int flag;
			scanf("%d",&flag);
			if (flag==1){
				scanf("%s",temp);
				sam.add(temp[0]-'a');
			}else{
				printf("%d\n",sam.ans);
			}
		}
	}
	return 0;
} 


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值