首页 > 其他 > 详细

UOJ523 半前缀计数

时间:2020-07-05 20:34:00      阅读:44      评论:0      收藏:0      [点我收藏+]

半前缀计数

设小写字母字符串 \(s\), 长度为 \(n\), \(s[l:r]\) 表示第 \(l\) 个到第 \(r\) 个字符构成的子串, \(l>r\) 时对应空串。

定义半前缀是 \(s[1:i] + s[j:k]\), 其中 \(0 \leq i < len(s), i < j \leq len(s),j-1 \leq k\leq len(s)\)。直观上来说,你可以把半前缀理解成某一个前缀 \(s[1:k]\) 删除掉某一个子串后形成的结果(当然也允许不删)。

给出字符串 \(s\),你需要求出 \(s\) 的所有半前缀中,有多少个不同的字符串。

题解

构造一个自动机,先贪心匹配前缀,再匹配后面的子串。

注意匹配了前缀\(1\sim i\)后,之后的子串不能以\(s_{i+1}\)开头。

乍看要写个Ukkonen算法,但是实际上自动机不用建出来。我们只需要知道\(i+1\sim n\)的不同子串数量以及以\(s_{i+1}\)开头的不同子串数量即可。

倒着建SAM,时间复杂度\(O(n)\)

CO int N=2e6;
int last=1,tot=1;
array<int,26> ch[N];
int fa[N],len[N];

void extend(int c){
	int x=last,cur=last=++tot;
	len[cur]=len[x]+1;
	for(;x and !ch[x][c];x=fa[x]) ch[x][c]=cur;
	if(!x) {fa[cur]=1; return;}
	int y=ch[x][c];
	if(len[y]==len[x]+1) {fa[cur]=y; return;}
	int clone=++tot;
	ch[clone]=ch[y],fa[clone]=fa[y],len[clone]=len[x]+1;
	fa[cur]=fa[y]=clone;
	for(;ch[x][c]==y;x=fa[x]) ch[x][c]=clone;
}

int64 sum,cnt[26];
char str[N];

int main(){
	scanf("%s",str+1);
	int n=strlen(str+1);
	int64 ans=1;
	for(int i=n;i>=1;--i){
		int c=str[i]-‘a‘;
		extend(c);
		sum+=len[last]-len[fa[last]],cnt[c]+=len[last]-len[fa[last]];
		ans+=1+sum-cnt[c];
	}
	printf("%lld\n",ans);
	return 0;
}

UOJ523 半前缀计数

原文:https://www.cnblogs.com/autoint/p/13251437.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!