zoukankan      html  css  js  c++  java
  • [NOI2018] 你的名字

    https://www.luogu.com.cn/problem/P4770

    题意

    给定字符串(S),多次询问,每次给出(l,r,T)询问不在(S)([l,r])区间出现过的(T)的本质不同的子串

    前68分满足(l=1,r=n)

    题解

    先考虑前68分,正难则反,即求出(T)(S)中出现的本质不同的子串

    我们定义(a[i])(T)长度为(i)的前缀在(S)中存在的最长后缀长度

    求法即对(S,T)建出后缀自动机,把(T)串放到(S)串的(SAM)上匹配

    然后还要自下而上的用儿子的(a)更新父亲的(a)

    最终答案,对于(T)(SAM)上的节点(i),它做出的贡献就是(len[i]-max(len[fa[i]],a[i]))

    对于长度小于等于(a[i])的后缀串因为在(T)串中存在所以没有了贡献

    以及(SAM)上每个点代表的本质不同的子串只有((len[fa],len[i]])这一段

    最终答案:(sum_{i=1}^{cnt}len[i]-max(len[fa[i]],a[i]))

    考虑100分与68分的区别在于

    我们无法分离出([l,r])(S)(SAM),然后把(T)串放到(S)串的(SAM)上匹配

    但是考虑(T)串在(S)([l,r])上匹配的时候需要的操作

    设当前匹配点为(root)

    1.查询(ch[root][c])([l,r])中是否存在

    2.跳到(fa[root])

    3.查询(len[root])

    所以,考虑维护(endpos)集合,采用线段树合并

    对于1操作就是判断该点一段区间是否有(endpos)

    对于二操作,因为父节点的(endpos)是当前点的超集,所以一定合法

    对于三操作,这个点的长度应该是(min(len,l-max(l,maxpos)+1))

    (maxpos)为对应区间的最大(endpos)

    但是我从上午打到了下午,有几个细节被恶心到了

    100分和68分只有在求出(a)的部分做出了些调整

    对于统计答案及其他一些地方,完全是等效的,所以我在此不再赘述

    线段树合并的时候,因为要保留每个线段树节点,而过去的合并操作会毁掉合并的两颗线段树

    所以要每次合并节点((x,y))都要合并给新建的新的一个节点(w)

    空间复杂度(感谢左手边的带神神)如果直接线段树合并是(O(nlog))的,但是有新建节点

    每次合并新建节点必定是((x,y))合并为(w)一个点,并且((x,y))不会再被合并了

    所以相当于每次由2个点合并成了1个点

    空间复杂度(sumlimits frac{nlog}{1}+frac{nlog}{2}+frac{nlog}{4}...=2nlog)

    在匹配判断当前点某个儿子,设为(son),是否存在于([l,r])时,找到了([l,r])的最大(endpos)后还要判断是否真的存在

    因为只有(endpos)的话长度小于等于(len[fa[son]])这个点也不是真实在这个区间里

    所以需要判断,合法条件是(pos>l+len[fa[son]-1)

    这个地方也太狗屎了

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=1e6+50;
    inline int rd(register int x=0,register char ch=getchar(),register int f=0){
    	for(;!isdigit(ch);ch=getchar()) f=ch=='-';
    	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-48;
    	return f?-x:x;
    }
    
    int n,m,lst,cnt,ptr;
    int rt[N],ls[N*20],rs[N*20],mx[N*20],ret[N];
    char s[N];
    int NEW(int o){
    	++ptr;ls[ptr]=ls[o],rs[ptr]=rs[o],mx[ptr]=mx[o];
    	return ptr;
    }
    void ins(int &x,int l,int r,int p){
    	x=NEW(x);mx[x]=max(mx[x],p);
    	if(l==r) return;int mid=(l+r)>>1;
    	p<=mid?ins(ls[x],l,mid,p):ins(rs[x],mid+1,r,p);
    }
    int merge(int x,int y,int l,int r){
    	if(!x||!y) return x+y;
    	if(l==r) return x;int mid=(l+r)>>1;
    	int w=NEW(x);
    	ls[w]=merge(ls[x],ls[y],l,mid);
    	rs[w]=merge(rs[x],rs[y],mid+1,r);
    	mx[w]=max(mx[ls[w]],mx[rs[w]]);
    	return w;
    }
    int ask(int x,int l,int r,int L,int R){
    	if((L<=l&&r<=R)||!x) return mx[x];
    	int mid=(l+r)>>1,Mx=0;
    	if(L<=mid) Mx=max(Mx,ask(ls[x],l,mid,L,R));
    	if(R>mid) Mx=max(Mx,ask(rs[x],mid+1,r,L,R));
    	return Mx;
    }
    struct SAM{
    	int n,lst,cnt;
    	int len[N],fa[N],buc[N],rk[N],id[N];
    	map<int,int> ch[N];
    	void extend(int c){
    		int p=lst,np=lst=++cnt;
    		len[np]=len[p]+1;
    		for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
    		if(!p) fa[np]=1;
    		else{
    			int q=ch[p][c];
    			if(len[q]==len[p]+1) fa[np]=q;
    			else{
    				int nq=++cnt;len[nq]=len[p]+1;fa[nq]=fa[q];fa[np]=fa[q]=nq; ch[nq]=ch[q];
    				for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
    			}
    		}
    		id[++id[0]]=np;
    	}
    	void build(int m){
    		clear(); n=m;
    		for(int i=1;i<=n;++i) extend(s[i]-'a');
    		for(int i=1;i<=cnt;++i) buc[len[i]]++;
    		for(int i=1;i<=n;++i) buc[i]+=buc[i-1];
    		for(int i=1;i<=cnt;++i) rk[buc[len[i]]--]=i;
    	}
    	void clear(){
    		for(int i=0;i<=cnt;++i){
    			len[i]=fa[i]=buc[i]=rk[i]=id[i]=0;
    			ch[i].clear();
    		}
    		lst=cnt=1;
    	}
    }F,G;
    int main(){
    	freopen("name.in","r",stdin);
    	freopen("name.out","w",stdout);
    	scanf("%s",s+1);n=strlen(s+1);F.build(n);
    	for(int i=1;i<=n;++i) ins(rt[F.id[i]],1,n,i);
    	for(int i=F.cnt;i;--i) if(F.fa[F.rk[i]]) rt[F.fa[F.rk[i]]]=merge(rt[F.fa[F.rk[i]]],rt[F.rk[i]],1,n);
    	for(int T=rd(),l,r;T;--T){
    		scanf("%s",s+1);m=strlen(s+1);G.build(m);
    		l=rd(),r=rd();
    		int root=1,Len=0;
    		ll ans=0;
    		for(int i=1;i<=m;++i){
    			while(1){
    				if(root==0) {root=1;Len=0;break;}
    				int pos=ask(rt[F.ch[root][s[i]-'a']],1,n,l,r);
    				if(pos>=l+F.len[F.fa[F.ch[root][s[i]-'a']]]) {root=F.ch[root][s[i]-'a'];Len=min(Len+1,pos-l+1);break;}
    				else {root=F.fa[root];Len=min(Len,F.len[root]);}
    			}
    			ret[G.id[i]]=min(G.len[G.id[i]],Len);
    		}
    		for(int i=G.cnt;i;--i) if(G.fa[G.rk[i]]) ret[G.fa[G.rk[i]]]=min(G.len[G.fa[G.rk[i]]],max(ret[G.fa[G.rk[i]]],ret[G.rk[i]]));
    		
    		for(int i=1;i<=G.cnt;++i) ans+=max(0,G.len[i]-max(G.len[G.fa[i]],ret[i]));
    		for(int i=0;i<=G.cnt;++i) ret[i]=0;
    		printf("%lld
    ",ans);
    	}
    }
    
  • 相关阅读:
    C#基础:ref和out的区别
    .NET Petshop详解(五):petshop输出缓存设置
    静态方法和实例化方法的区别
    .Net Petshop详解(一):petshop概览和准备工作
    什么是MVC(三层架构)
    C# 反射入门知识
    C#类与对象
    linux用户权限的管理
    PHP pear安装
    shell神器curl用法笔记
  • 原文地址:https://www.cnblogs.com/hzoi2018-xuefeng/p/13093761.html
Copyright © 2011-2022 走看看