• 后缀数组


    教程

    https://www.cnblogs.com/victorique/p/8480093.html

    论文(下面的题目论文里基本上都有)

    https://wenku.baidu.com/view/5b886b1ea76e58fafab00374.html

    Part 1

    A. 不重叠最长重复子串

    直接求 (max{height[i]}) 即可

    B. 重叠 (k) 次最长重复子串

    二分答案子串的长度,把题目变成判定性问题,按照 (height[i]>=mid)(height) 数组分成若干块,最后统计出现次数是否 (le k)

    C. 一个字符串中不同子串个数

    对于每个后缀它的贡献是 (n-sa[i]+1-height[i])

    D. 一个字符串的最长回文子串

    将该字符串reverse后接在原字符串后面,用'$'连接,再求最长公共子串。(见下)

    E. 连续重复子串

    (nle 10^6)

    //SA
    for(int i=1;i<=n;i++)if(n%i==0&&lcp(1,i+1)==n-i){printf("%d
    ",n/i);break;}
    

    (O(nlog n))不能通过本题

    //kmp
    printf("%d
    ",n%(n-nxt[n])==0?n/(n-nxt[n]):1);
    

    (O(n)) ,可以通过本题

    F. 一个串重复次数最多的连续重复子串

    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n-i;j+=i){
    		int pos=j-i+modulo(j+i+lcp(j,j+i)-1,i);
    		if(pos<=0)pos=1;
    		int tmp=lcp(pos,pos+i)/i+1;
    		if(tmp>ans)ans=tmp,len[cnt=1]=i;
    		else if(tmp==ans&&len[cnt]!=i)len[++cnt]=i;
    	}
    }
    bool flag=0;
    for(int i=1;i<=n&&!flag;i++){
    	for(int j=1;j<=cnt&&sa[i]+len[j]<=n&&!flag;j++){
    		if(lcp(sa[i],sa[i]+len[j])>=(ans-1)*len[j]){
    			printf("Case %d: ",++T);
    			for(int l=sa[i];l<=sa[i]+len[j]*ans-1;l++)putchar(s[l]);
    			puts("");
    			flag=1;
    		}
    	}
    }
    

    好dark的算法,复杂度证不来,反正过了就行

    G. 最长公共子串

    首先将两个字符串拼成一个,如果 (sa[i-1])(sa[i]) 分局两侧就更新答案。

    H. 两个字符串的长度 (≥ k) 的最长公共子串个数

    重要

    (height) 数组按 (height[i]>=k) 分成若干块,然后用单调栈扫。(cnt[i]) 维护的是在 (i) 之前最靠近 (i) 的一个大于等于 (height[i]) 的连通块。

    e.g.

    假设我们现在的height数组是长这样的:

    (i) 之前的栈:1 5 6 8

    现在第9个元素将把5,6,8弹出栈,图片上是弹出5的情况

    当弹出完成时,就只剩下 (i-cnt[i])(i-1)(i) 的贡献了。

    Code

    int main(){
    	while(scanf("%d",&k),k){
    		scanf("%s",s+1);
    		len=strlen(s+1);
    		s[len+1]='!';
    		scanf("%s",s+len+2);
    		n=strlen(s+1);
    		sufsort();
    		geth();
    		int top=0;
    		long long sum=0,ans=0;
    		for(int i=1;i<=n;i++){
    			if(height[i]>=k){
    				int tot=0;
    				if(sa[i-1]<=len)tot++,sum+=(long long)height[i]-k+1;
    				while(top&&height[i]<=height[stk[top]]){
    					tot+=cnt[stk[top]];
    					sum-=(long long)cnt[stk[top]]*(height[stk[top]]-height[i]);
    					top--;
    				}
    				cnt[i]=tot;
    				stk[++top]=i;
    				if(sa[i]>len)ans+=sum;
    			}
    			else top=sum=0;
    		}
    		top=sum=0;
    		for(int i=1;i<=n;i++){
    			if(height[i]>=k){
    				int tot=0;
    				if(sa[i-1]>len)tot++,sum+=(long long)height[i]-k+1;
    				while(top&&height[i]<=height[stk[top]]){
    					tot+=cnt[stk[top]];
    					sum-=(long long)cnt[stk[top]]*(height[stk[top]]-height[i]);
    					top--;
    				}
    				cnt[i]=tot;
    				stk[++top]=i;
    				if(sa[i]<=len)ans+=sum;
    			}
    			else top=sum=0;
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    I. 有 (n) 个字符串,出现在至少 (n/2) 个字符串中的最长子串

    (nle 100,lengthle 1000)

    先二分长度,再分块,再用一个bool[]在哪几个字符串出现过。

    J. 求 (n) 个字符串中在每个字符串出现至少两次的最长子串

    (nle 10,lengthle 10000)

    二分长度,开一个bool[],统计

    K. 求 (sum_{1le i<jle n}lcp(Suffix_i,Suffix_j))

    he
    单调栈优化dp

    Code

    int main(){
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	sufsort();
    	geth();
    	int top=0;
    	long long ans=0;
    	for(int i=1;i<=n;i++){
    		while(top&&height[i]<=height[stk[top]])top--;
    		if(top)dp[i]=(long long)(i-stk[top])*height[i]+dp[stk[top]];
    		ans+=dp[i];
    		stk[++top]=i;
    	}
    	printf("%lld
    ",(long long)(n-1)*n*(n+1)/2-2*ans);
    	return 0;
    }
    

    Part 2

    A

    给你26个英文字母是好的还是坏的,现在给你一个长度 (le 1500) 的字符串,问至多存在 (k) 个坏字母的子串有多少个。

    用后缀数组或者其它方法 (n^2) 暴力搞一下就行了

    D

    有三个字符串。你需要确定对于每个 (l (1 ≤ l ≤ min(|s_1|, |s_2|, |s_3|)) 有多少三元组 ((i_1, i_2, i_3)) ,满足三个 (s_k[i_k... i_{k + l - 1}] (k = 1, 2, 3)) 都相等。膜 (10^9+7)

    cmp(int x,int y){return height[x]>height[y];}
    main(){
    	for(int i=1;i<=n;i++)a[i]=i,f[i]=i;
    	for(int i=1;i<=len[1];i++)sum[i][1]=1;
    	for(int i=len[1]+2;i<=len[2];i++)sum[i][2]=1;
    	for(int i=len[2]+2;i<=len[3];i++)sum[i][3]=1;
    	sort(a+1,a+n+1,cmp);
    	long long ans=0;
    	for(int i=k,j=1;i>=1;i--){
    		for(;j<=n&&height[a[j]]>=i;j++){
    			int l=find(sa[a[j]-1]),r=find(sa[a[j]]);
    			ans=((ans-(long long)sum[l][1]*sum[l][2]*sum[l][3]%mod+mod)%mod-(long long)sum[r][1]*sum[r][2]*sum[r][3]%mod+mod)%mod;
    			sum[l][1]+=sum[r][1],sum[l][2]+=sum[r][2],sum[l][3]+=sum[r][3];
    			ans=(ans+(long long)sum[l][1]*sum[l][2]*sum[l][3]%mod)%mod;
    			f[r]=l;
    		}
    		ANS[i]=ans;
    	}
    	ios::sync_with_stdio(0);
    	for(int i=1;i<=k;i++)cout<<ANS[i]<<' ';
    }
    

    E

    有一个字符串和 (q) 组询问,每组询问有两个数组 (a_1, a_2, dots, a_k)(b_1, b_2, dots, b_l) ,计算 (sumlimits_{i = 1}^{i = k} sumlimits_{j = 1}^{j = l}{ ext{LCP}(s[a_i dots n], s[b_j dots n])})

    和上面的H题大同小异

    bool cmp(const SS& x,const SS& y){return rnk[x.a]==rnk[y.a]?x.mo<y.mo:rnk[x.a]<rnk[y.a];}
    int main(){
    	for(int i=2;i<maxn;i++)lg[i]=lg[i-1]+((1<<(lg[i-1]+1))==i);
    	int Q;
    	scanf("%d%d%s",&n,&Q,s+1);
    	sufsort();
    	geth();
    	initst();
    	while(Q--){
    		int cnta,cntb,top=0;
    		long long ans=0,sum=0;
    		scanf("%d%d",&cnta,&cntb);
    		for(int i=1;i<=cnta;i++){int x;scanf("%d",&x);ss[i]=SS(x,0);}
    		for(int i=1;i<=cntb;i++){int x;scanf("%d",&x);ss[i+cnta]=SS(x,1);}
    		sort(ss+1,ss+cnta+cntb+1,cmp);
    		for(int i=1;i<=cnta+cntb;i++){
    			int tot=0;
    			if(!ss[i-1].mo)tot++,sum+=lcp(ss[i].a,ss[i-1].a);
    			while(top&&lcp(ss[i].a,ss[i-1].a)<=lcp(ss[stk[top]].a,ss[stk[top]-1].a)){
    				tot+=cnt[stk[top]];
    				sum-=(long long)cnt[stk[top]]*(lcp(ss[stk[top]].a,ss[stk[top]-1].a)-lcp(ss[i].a,ss[i-1].a));
    				top--;
    			}
    			cnt[i]=tot;
    			stk[++top]=i;
    			if(ss[i].mo)ans+=sum;
    		}
    		top=sum=0;
    		for(int i=1;i<=cnta+cntb;i++){
    			int tot=0;
    			if(ss[i-1].mo)tot++,sum+=lcp(ss[i].a,ss[i-1].a);
    			while(top&&lcp(ss[i].a,ss[i-1].a)<=lcp(ss[stk[top]].a,ss[stk[top]-1].a)){
    				tot+=cnt[stk[top]];
    				sum-=(long long)cnt[stk[top]]*(lcp(ss[stk[top]].a,ss[stk[top]-1].a)-lcp(ss[i].a,ss[i-1].a));
    				top--;
    			}
    			cnt[i]=tot;
    			stk[++top]=i;
    			if(!ss[i].mo)ans+=sum;
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    F

    (a) 为字符串的一个子串, (f(a))(a) 在字符串中出现的次数,但是 (a) 不能以字符串中的某些位置结尾,求最大的 (|a|*f(a))

    把字符串反转一下,问题变为不能以字符串中的某些位置开头,然后就变成了单调栈问题。

    int main(){
    	scanf("%d%s%s",&n,s+1,b+1);
    	reverse(s+1,s+n+1),reverse(b+1,b+n+1);
    	sufsort();
    	geth();
    	long long ans=0;
    	int top=0;
    	for(int i=1;i<=n;i++){
    		if(b[sa[i-1]]=='0')cnt[i]++;
    		while(top&&height[i]<=height[stk[top]]){
    			cnt[i]+=cnt[stk[top]];
    			ans=max(ans,(long long)cnt[i]*height[stk[top]]);
    			top--;
    		}
    		stk[++top]=i;
    	}
    	if(b[sa[n]]=='0')cnt[n+1]++;
    	while(top){
    		cnt[n+1]+=cnt[stk[top]];
    		ans=max(ans,(long long)cnt[n+1]*height[stk[top]]);
    		top--;
    	}
    	for(int i=1;i<=n;i++)if(b[i]=='0'){ans=max(ans,(long long)n-i+1);break;}
    	cout<<ans<<endl;
    	return 0;
    }
    

    B

    struct data{
    	int val,len;
    	data():val(0),len(0){}
    	data(int _v,int _l):val(_v),len(_l){}
    	friend const data& max(const data& x,const data& y){return (x.val==y.val?x.len<y.len:x.val>y.val)?x:y;}
    };
    struct node{data a,z;int mi;}t[maxn<<2];
    void pushup(int p){t[p].a=max(t[p<<1].a,t[p<<1|1].a),t[p].mi=min(t[p<<1].mi,t[p<<1|1].mi);}
    void pushdown(int p,int l,int r){
    	if(l==r)return;
    	if(t[p].z.val||t[p].z.len){
    		t[p<<1].a=max(t[p<<1].a,t[p].z),t[p<<1|1].a=max(t[p<<1|1].a,t[p].z);
    		t[p<<1].z=max(t[p<<1].z,t[p].z),t[p<<1|1].z=max(t[p<<1|1].z,t[p].z);
    		t[p].z=data();
    	}
    }
    void build(int p,int l,int r){
    	t[p].mi=INF;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	build(p<<1,l,mid);
    	build(p<<1|1,mid+1,r);
    }
    void change(int p,int l,int r,int seg_l,int seg_r,const data& k){
    	pushdown(p,l,r);
    	if(seg_l<=l&&r<=seg_r){t[p].a=max(t[p].a,k);t[p].z=k;return;}
    	int mid=(l+r)>>1;
    	if(seg_l<=mid)change(p<<1,l,mid,seg_l,seg_r,k);
    	if(seg_r>mid)change(p<<1|1,mid+1,r,seg_l,seg_r,k);
    	pushup(p);
    }
    void change(int p,int l,int r,int pos,int k){
    	pushdown(p,l,r);
    	if(l==r){t[p].mi=k;return;}
    	int mid=(l+r)>>1;
    	if(pos<=mid)change(p<<1,l,mid,pos,k);
    	else change(p<<1|1,mid+1,r,pos,k);
    	pushup(p);
    }
    const data& query(int p,int l,int r,int pos){
    	pushdown(p,l,r);
    	if(l==r)return t[p].a;
    	int mid=(l+r)>>1;
    	if(pos<=mid)return query(p<<1,l,mid,pos);
    	else return query(p<<1|1,mid+1,r,pos);
    }
    int query(int p,int l,int r,int seg_l,int seg_r){
    	pushdown(p,l,r);
    	if(seg_l<=l&&r<=seg_r)return t[p].mi;
    	int mid=(l+r)>>1,ret=INF;
    	if(seg_l<=mid)ret=min(ret,query(p<<1,l,mid,seg_l,seg_r));
    	if(seg_r>mid)ret=min(ret,query(p<<1|1,mid+1,r,seg_l,seg_r));
    	return ret;
    }
    pair<int,int> binary_search(int pos,int len){
    	int L,R,l=1,r=pos,mid;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(lcp(mid,pos)>=len)L=mid,r=mid-1;
    		else l=mid+1;
    	}
    	l=pos,r=n;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(lcp(pos,mid)>=len)R=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return make_pair(L,R);
    }
    int main(){
    	scanf("%d%s",&n,s+1);
    	sufsort(),geth(),initst();
    	build(1,1,n);
    	pair<int,int> tmp;
    	int ans=0;
    	for(int i=n;i>=1;i--){
    		int val,len,l,r;
    		data x=query(1,1,n,rnk[i]);
    		if(x.val==0)val=len=1;
    		else{
    			val=x.val+1;
    			tmp=binary_search(rnk[i],x.len),l=tmp.first,r=tmp.second;
    			len=query(1,1,n,l,r)+x.len-i;
    		}
    		change(1,1,n,rnk[i],i);
    		tmp=binary_search(rnk[i],len),l=tmp.first,r=tmp.second;
    		change(1,1,n,l,r,data(val,len));
    		ans=max(ans,val);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    C

    bool check(int i,int length){
    	int L=i,R=i,l=1,r=i-1,mid;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(lcp(mid,i)>=length)L=mid,r=mid-1;//,o(mid);
    		else l=mid+1;
    	}
    	l=i+1,r=n;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(lcp(i,mid)>=length)R=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return lef[R]>=L;
    }
    int main(){
    	scanf("%d%d",&N,&k);
    	if(k>N){for(int i=1;i<=N;i++)printf("0 ");return 0;}
    	len[0]=-1;
    	for(int i=1;i<=N;i++){
    		if(i>1)s[len[i-1]+1]=i-100001;
    		scanf("%s",str+1);
    		len[i]=len[i-1]+1+strlen(str+1);
    		for(int j=len[i-1]+2;j<=len[i];j++)s[j]=str[j-len[i-1]-1],p[j]=i;
    	}
    	n=len[N];
    	sufsort();
    	geth();
    	initst();
    	for(int i=1;i<=n;i++)buc[i]=0;
    	for(int i=N,j=0,tot=buc[p[sa[i]]]=1;i<=n;i++,tot+=!buc[p[sa[i]]],buc[p[sa[i]]]++){
    		for(;j<=i&&tot>=k;buc[p[sa[j]]]--,tot-=!buc[p[sa[j]]],j++);
    		if(j)j--,tot+=!buc[p[sa[j]]],buc[p[sa[j]]]++;
    		lef[i]=j;
    	}
    	long long ans=0;
    	ios::sync_with_stdio(0);
    	for(int i=1,length=0;i<=n;i++){
    		if(p[i]==0)cout<<ans<<' ',ans=0,length=0;
    		else{
    			if(length)length--;
    			for(;i+length-1<=len[p[i]]&&check(rnk[i],length);length++);
    			length--;
    			ans+=length;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    Attributes in C#
    asp.net C# 时间格式大全
    UVA 10518 How Many Calls?
    UVA 10303 How Many Trees?
    UVA 991 Safe Salutations
    UVA 10862 Connect the Cable Wires
    UVA 10417 Gift Exchanging
    UVA 10229 Modular Fibonacci
    UVA 10079 Pizza Cutting
    UVA 10334 Ray Through Glasses
  • 原文地址:https://www.cnblogs.com/BlogOfchc1234567890/p/10676950.html
Copyright © 2020-2023  润新知