• UOJ523 半前缀计数


    半前缀计数

    设小写字母字符串 (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 kleq len(s))。直观上来说,你可以把半前缀理解成某一个前缀 (s[1:k]) 删除掉某一个子串后形成的结果(当然也允许不删)。

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

    题解

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

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

    乍看要写个Ukkonen算法,但是实际上自动机不用建出来。我们只需要知道(i+1sim 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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    编程心得2----有疑惑,用代码说话
    Java的static关键字
    html中设置textbox的宽和高
    C#(winform)设置窗体的启动位置
    C#(winform)设置窗口置顶
    C#(winform)实现不同DPI控件自适应1
    设计模式--策略模式(strategy)
    requirejs原理深究以及r.js和gulp的打包【转】
    JavaScript中清空数组的三种方式
    localstorage本地存储
  • 原文地址:https://www.cnblogs.com/autoint/p/13251437.html
Copyright © 2020-2023  润新知