• BZOJ3756 Pty的字符串


    Pty的字符串

    在神秘的东方有一棵奇葩的树,它有一个固定的根节点(编号为1)。树的每条边上都是一个字符,字符为a,b,c中的一个,你可以从树上的任意一个点出发,然后沿着远离根的边往下行走,在任意一个节点停止,将你经过的边的字符依次写下来,就能得到一个字符串,例如:

    在这棵树中我们能够得到的字符串是:
    c, cb, ca, a, b, a

    现在pty得到了一棵树和一个字符串S。如果S的一个子串[l,r]和树上某条路径所得到的字符串完全相同,则我们称这个子串和该路径匹配。现在pty想知道,S的所有子串和树上的所有路径的匹配总数是多少?

    N<=800000
    树的最大深度<=800000

    分析

    我们可以先对trie树建出广义SAM,然后维护一下right集合大小(注意right集合在广义SAM上的维护方式)。

    然后把匹配穿往广义SAM上匹配,假设现在匹配到了x节点,那么x的所有祖先可以被匹配上,那么一个节点的贡献即为right[x]*(len-l[fa[x]])+sum[fa[x]]。其中len为匹配长度。

    维护所有祖先的和,即sum[x]。

    时间复杂度(O(n+m))

    代码

    拓扑排序必须老实来。

    co int N=8e5+1;
    int last=1,tot=1,head[N]={0,1};
    int ch[N*2][3],fa[N*2],len[N*2],siz[N*2];
    void extend(int c){
    	int p=last,cur=last=++tot;
    	len[cur]=len[p]+1,siz[cur]=1;
    	for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
    	if(!p) fa[cur]=1;
    	else{
    		int q=ch[p][c];
    		if(len[q]==len[p]+1) fa[cur]=q;
    		else{
    			int clone=++tot;
    			memcpy(ch[clone],ch[q],sizeof ch[q]);
    			fa[clone]=fa[q],len[clone]=len[p]+1;
    			fa[cur]=fa[q]=clone;
    			for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
    		}
    	}
    }
    int n,m,deg[N*2],ord[N*2];
    ll f[N*2],ans;
    char s[N*10];
    int main(){
    	read(n);
    	for(int i=2,x,y;i<=n;++i){
    		read(x);for(y=getchar();!isalpha(y);y=getchar());
    		last=head[x],extend(y-'a'),head[i]=last;
    	}
    	for(int i=1;i<=tot;++i) ++deg[fa[i]];
    	int L=1,R=0;
    	for(int i=1;i<=tot;++i)if(!deg[i]) ord[++R]=i;
    	for(int p;L<=R;++L){
    		p=ord[L];
    		siz[fa[p]]+=siz[p];
    		if(--deg[fa[p]]==0) ord[++R]=fa[p];
    	}
    	for(int i=tot;i;--i) {
    		int p=ord[i];
    		f[p]=f[fa[p]]+(ll)(len[p]-len[fa[p]])*siz[p];
    	}
    //	for(int i=1;i<=tot;++i)
    //		std::cerr<<i<<" f="<<f[i]<<std::endl;
    	scanf("%s",s+1),m=strlen(s+1);
    	for(int i=1,p=1,l=0;i<=m;++i){
    		s[i]-='a';
    		while(p&&!ch[p][s[i]]) p=fa[p],l=len[p]; // edit1: change l meanwhile
    		if(!p) p=1,l=0;
    		else p=ch[p][s[i]],++l,ans+=f[fa[p]]+(ll)(l-len[fa[p]])*siz[p];
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Ant属性文件
    使用numpy处理数组
    机器学习实战之决策树(一)
    列表去掉重复元素
    cv2.putText,cv2.rectangle方法
    sklearn 中文文档
    numpy delete方法
    MATLAB ~的用法
    MATLAB 基础
    Python 遍历目录下的子目录和文件
  • 原文地址:https://www.cnblogs.com/autoint/p/10878561.html
Copyright © 2020-2023  润新知