首页 > 其他 > 详细

6567. 【GDOI2020模拟】字符串(string)

时间:2020-04-27 23:26:01      阅读:95      评论:0      收藏:0      [点我收藏+]

题目描述

你喜欢字符串。有人送了你一个仅含小写字母的字符串。
由于你是一名优秀的 ????er,所以你决定对这个字符串展开研究。
定义两个字符串是相似的,当且仅当存在至多一个 ?? ,使得这两个字符串中只有第 ?? 个字母不同。
你取出了这个字符串中所有长度为m 的子串。你想知道,对于每个长度为 m 的子串,有多少个其它长度为 m 的子串与它相似。

n<=10^5 1<=m<=n

题解

dh牛逼,不接受反驳

省选前的SA复习

若ij相似,则lcp+lcs>=m-1

建出前缀&后缀SA,在前缀的height上分治

每次按min(hi)分开,枚举较小的那一边来搞

因为lcp确定了,所以等于找lcs>=m-1-lcp,对应到后缀height是一段区间

那么线段树维护后缀height,小的那边可以直接查询

对于大的那边,在处理每个小的的时候在线段树上打tag最后查询

因为tag的意义是对当前线段树的修改,所以合并时先下传再合并

code

想是一回事,打起来又是另一回事了

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define ll long long
#define file
using namespace std;

int D2[100001],Rk[100001],p[17],P[100001],ans[100001],n,m,i,j,k,l,id;
char ch;
vector<int> D[100001];
struct String{
	int rk[100002],sa[100001],hi[100001],h[100001],a[100001],rmq[100001][17],Rmq[100001][17],x,y;
	
	void init()
	{
		fo(i,1,n) rk[i]=a[i];
		
		l=1;
		while (l<n)
		{
			k=0;
			fo(i,1,n) D[rk[min(i+l,n+1)]].push_back(i);
			fo(i,0,n) while (!D[i].empty()) D2[++k]=D[i].back(),D[i].pop_back();
			fd(i,n,1) D[rk[D2[i]]].push_back(D2[i]);
			
			memcpy(Rk,rk,(n+1)*4);k=0;
			fo(i,0,n)
			{
				x=y=0;
				while (!D[i].empty())
				{
					id=D[i].back();k+=x!=Rk[id] || y!=Rk[min(id+l,n+1)];x=Rk[id],y=Rk[min(id+l,n+1)];
					rk[id]=k;D[i].pop_back();
				}
			}
			l*=2;
		}
		fo(i,1,n) sa[rk[i]]=i;
		
		fo(i,1,n)
		if (rk[i]<n)
		{
			h[i]=min(max(h[i-1]-1,0),min(n-i+1,n-sa[rk[i]+1]+1));
			while (h[i]<min(n-i+1,n-sa[rk[i]+1]+1) && a[i+h[i]]==a[sa[rk[i]+1]+h[i]]) ++h[i];
		}
		else h[i]=0;
		fo(i,1,n) if (rk[i]<n) hi[rk[i]]=h[i];
		
//		---
		
		fo(i,1,n-1) rmq[i][0]=hi[i],Rmq[i][0]=i;
		fo(i,1,16)
		{
			fo(j,1,(n-1)-p[i]+1)
			if (rmq[j][i-1]<rmq[j+p[i-1]][i-1])
			rmq[j][i]=rmq[j][i-1],Rmq[j][i]=Rmq[j][i-1];
			else
			rmq[j][i]=rmq[j+p[i-1]][i-1],Rmq[j][i]=Rmq[j+p[i-1]][i-1];
		}
	}
	
	pair<int,int> get(int x,int y) //after sorting
	{
		int s1,s2;
		if (x==y) return pair<int,int>(n-sa[x]+1,0);
		
		--y;
		if (rmq[x][P[y-x+1]]<rmq[y-p[P[y-x+1]]+1][P[y-x+1]])
		s1=rmq[x][P[y-x+1]],s2=Rmq[x][P[y-x+1]];
		else
		s1=rmq[y-p[P[y-x+1]]+1][P[y-x+1]],s2=Rmq[y-p[P[y-x+1]]+1][P[y-x+1]];
		
		return pair<int,int>(s1,s2);
	}
} a,b;
struct tree{
	int tr[7000001][3],Tr[7000001],len;
	
	void New(int t,int x) {if (!tr[t][x]) tr[t][x]=++len;}
	void down(int t,int l,int r)
	{
		if (Tr[t])
		{
			if (l<r) Tr[tr[t][0]]+=Tr[t],Tr[tr[t][1]]+=Tr[t];
			else {if (b.sa[l]<=n-m+1) ans[n-b.sa[l]+1-m+1]+=Tr[t];}
			Tr[t]=0;
		}
	}
	void change(int t,int l,int r,int x)
	{
		int mid=(l+r)/2;
		++tr[t][2];
		if (l==r) return;
		
		if (x<=mid) New(t,0),change(tr[t][0],l,mid,x);
		else New(t,1),change(tr[t][1],mid+1,r,x);
	}
	void Change(int t,int l,int r,int x,int y)
	{
		int mid=(l+r)/2;
		if (x<=l && r<=y) {++Tr[t];return;}
		
		if (x<=mid && tr[t][0]) Change(tr[t][0],l,mid,x,y);
		if (mid<y && tr[t][1]) Change(tr[t][1],mid+1,r,x,y);
	}
	int find(int t,int l,int r,int x,int y)
	{
		int mid=(l+r)/2,ans=0;
		if (x<=l && r<=y) return tr[t][2];
		
		if (x<=mid && tr[t][0]) ans+=find(tr[t][0],l,mid,x,y);
		if (mid<y && tr[t][1]) ans+=find(tr[t][1],mid+1,r,x,y);
		
		return ans;
	}
	void merge(int t1,int t2,int l,int r)
	{
		int mid=(l+r)/2;
		down(t1,l,r),down(t2,l,r);
		if (l==r) {tr[t1][2]+=tr[t2][2];return;}
		
		if (tr[t1][0] && tr[t2][0])
		merge(tr[t1][0],tr[t2][0],l,mid);
		else
		if (tr[t2][0])
		tr[t1][0]=tr[t2][0];
		
		if (tr[t1][1] && tr[t2][1])
		merge(tr[t1][1],tr[t2][1],l,mid);
		else
		if (tr[t2][1])
		tr[t1][1]=tr[t2][1];
		
		tr[t1][2]=tr[tr[t1][0]][2]+tr[tr[t1][1]][2];
	}
	void dfs(int t,int l,int r)
	{
		int mid=(l+r)/2;
		down(t,l,r);
		if (l==r) return;
		
		if (tr[t][0]) dfs(tr[t][0],l,mid);
		if (tr[t][1]) dfs(tr[t][1],mid+1,r);
	}
} tr;
void swap(int &x,int &y) {int z=x;x=y;y=z;}

int work(int x,int y)
{
	if (x==y) {if (a.sa[x]<=n-m+1) tr.change(x,1,n,b.rk[n-a.sa[x]+1-m+1]); return x;}
	
	pair<int,int> S=a.get(x,y);
	int s1=S.first,s2=S.second,mid,i,j,k,l,r,L,R,t1=work(x,s2),t2=work(s2+1,y),st,ed;
	
	if ((s2-x+1)*2<=y-x+1)
	st=x,ed=s2;
	else
	st=s2+1,ed=y,swap(t1,t2);
	
	fo(i,st,ed)
	if (a.sa[i]<=n-m+1)
	{
		l=1;r=b.rk[n-a.sa[i]+1-m+1];
		while (l<r)
		{
			mid=(l+r)/2;
			if (b.get(mid,b.rk[n-a.sa[i]+1-m+1]).first>=m-1-s1)
			r=mid; else l=mid+1;
		}
		L=l;
		l=b.rk[n-a.sa[i]+1-m+1];r=n;
		while (l<r)
		{
			mid=(l+r)/2;
			if (b.get(b.rk[n-a.sa[i]+1-m+1],mid).first>=m-1-s1)
			l=mid+1; else r=mid;
		}
		if (!(b.get(b.rk[n-a.sa[i]+1-m+1],l).first>=m-1-s1)) --l;
		R=l;
		
		ans[a.sa[i]]+=tr.find(t2,1,n,L,R);
		tr.Change(t2,1,n,L,R);
	}
	
	tr.merge(t1,t2,1,n);
	return t1;
}

int main()
{
	freopen("string.in","r",stdin);
	#ifdef file
	freopen("string.out","w",stdout);
	#endif
	
	p[0]=1;fo(i,1,16) p[i]=p[i-1]*2;
	fo(i,1,100000) P[i]=floor(log2(i));
	
	scanf("%d%d",&n,&m);
	fo(i,1,n)
	{
		ch=getchar();
		while (ch<‘a‘ || ch>‘z‘) ch=getchar();
		a.a[i]=ch-‘a‘+1;
	}
	fd(i,n,1) b.a[i]=a.a[n-i+1];
	
	a.init();b.init();
	tr.len=n;
	tr.dfs(work(1,n),1,n);
	
	fo(i,1,n-m+1) printf("%d ",ans[i]);
	printf("\n");
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}

6567. 【GDOI2020模拟】字符串(string)

原文:https://www.cnblogs.com/gmh77/p/12790743.html

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