#1762. 识别子串(string)

题意

内存限制:128 MiB
时间限制:1000 ms

在这里插入图片描述
L ≤ 100000 L \leq 100000 L100000

题解

考虑建出parent树,只有叶子结点上所接受的串在原串中出现一次,而其最长接受的串为原串的一段前缀
所以对于其叶子结点 i i i 上在原串中对应着 [ 1 , l e n i ] [1,len_i] [1,leni]~ [ l e n i − l e n f a i , l e n i ] [len_i-len_{fa_i},len_i] [lenilenfai,leni]
所以对于 [ 1 , l e n i − l e n f a i ] [1,len_i-len_{fa_i}] [1,lenilenfai] 的点,可以把 l e n i len_i leni 作为其右端点,对于 [ l e n i − l e n f a i + 1 , l e n i ] [len_i-len_{fa_i}+1,len_i] [lenilenfai+1,leni] 的点,可以把 l e n f a i + 1 len_{fa_i}+1 lenfai+1 作为包含它的区间长度
所以开两棵线段树,维护最小右端点和最小长度即可

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,lst=1,sz=1,ans[N];
char s[N];bool g[N];
struct SAM{
	int link,len;
	map<int,int>nx;
}a[N];
void build(int x){
	int np=++sz,p=lst;
	a[np].len=a[p].len+1;
	while(p && !a[p].nx.count(x))
		a[p].nx[x]=np,p=a[p].link;
	if (!p) a[np].link=1;
	else{
		int q=a[p].nx[x];
		if (a[q].len==a[p].len+1)
			a[np].link=q;
		else{
			int nq=++sz;
			a[nq].len=a[p].len+1;
			a[nq].link=a[q].link;
			a[nq].nx=a[q].nx;
			a[q].link=a[np].link=nq;
			while(p && a[p].nx[x]==q)
				a[p].nx[x]=nq,p=a[p].link;
		}
	}
	lst=np;
}
struct T{
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
	int in[N*2];
	void build(int k,int l,int r){
		in[k]=1e9;if (l==r) return;
		build(Ls,l,mid);build(Rs,mid+1,r);
	}
	void update(int k,int l,int r,int L,int R,int v){
		if (L>R) return;
		if (L<=l && r<=R){in[k]=min(in[k],v);return;}
		if (mid>=L) update(Ls,l,mid,L,R,v);
		if (mid<R) update(Rs,mid+1,r,L,R,v);
	}
	int query(int k,int l,int r,int x,int v){
		if (l==r) return min(v,in[k]);
		if (mid>=x) return query(Ls,l,mid,x,min(v,in[k]));
		return query(Rs,mid+1,r,x,min(v,in[k]));
	}
}t[2];
int main(){
	scanf("%s",s+1);n=strlen(s+1);
	for (int i=1;i<=n;i++) build(s[i]-'a');
	for (int i=1;i<=sz;i++) g[a[i].link]=1;
	t[0].build(1,1,n);t[1].build(1,1,n);
	for (int l,r,i=1;i<=sz;i++) if (!g[i])
		r=l=a[i].len,l-=a[a[i].link].len,
		t[0].update(1,1,n,1,l-1,r),
		t[1].update(1,1,n,l,r,r-l+1);
	for (int i=1;i<=n;i++)
		printf("%d\n",min(t[0].query(1,1,n,i,1e9)-i+1,t[1].query(1,1,n,i,1e9)));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值