UOJ608. 【UR #20】机器蚤分组

一个字符串的答案,是把它的所有子串分组,满足每组中两两之间互为子串包含关系,最小的组数。

给一个字符串。若干个询问,每次询问一个区间的答案。

(n,qle 10^5)


暴力:建出后缀树,在这棵树上建后缀自动机。把转移边和fail边都丢进图中,然后跑最小链覆盖。跑一次时间是(O(n^3))

最小链覆盖等于最长反链。

结论1:一定存在一条最长反链,使得反链上的字符串长度都相同。

证明:如果现在找到了一条最长反链,且字符串长度不全相同,则找到最小的(len),找长度为(len)的字符串(s[l,r]),满足(s[l-1,r-1])(s[l+1,r+1])不在反链中。一定存在。(假设是(s[l+1,r+1])不在反链中)

(s[l,r+1])不在反链中,而它包含的子串是(s[l,r])的子串并(s[ldots r+1,r+1]),由于(len)最小,不存在(s[l+2dots r+1,r+1])的子串。于是可以把(s[l,r])替换成(s[l,r+1])

一直这样做下去,最终可以使长度相同。

结论2:如果最长反链长度(ge k),当且仅当长度(n-k+1)的子串互不相同。

证明:充分性显然,接下来证必要性。

只考虑字符串长度相等的最长反链,设字符串长度为(len)。此时有(n-len+1ge k)

反证,假设长度(n-k+1)的子串存在相同,设分别为(s[a,a+n-k],s[b,b+n-k])。则两个子串中长度为(len)的子串对应相同,即(s[a+i,a+i+len-1]=s[b+i,b+i+len-1],i+len-1le n-k)。于是对于(len)来说,本质不同的子串不超过(n-len+1-((n-k)-(len-1)+1)=k-1),矛盾。

推论:答案为:(n-max_{i<j} LCP(S[i:],S[j:]))

相当于找到最大(k)满足(n-k+1)的子串互不相同。

也就是最小(len),子串互不相同,答案为(n-len+1)。此时(len-1),子串存在相同。

子串存在相同,即存在两个长度等于(len-1)的子串相同。

然后好做了。按照套路SAM+LCT搞,枚举右端点,然后access。LCT中记录最后一次的右端点。另外用个线段树,支持给([l,r])中每个位置(x),和(k-x)(max)。修改时候,相当于找到上一次的endpos,它对应的区间([endpos-maxlen+1,endpos-minpos+1])每个位置(x)(endpos+1-x)(max)。时间(O(nlg^2 n))

题解给出个不错的想法:在后缀树上对endpos集合启发式合并。合并之后,新形成的邻居(即合并(S_1,S_2)(xin S_1,yin S_2)(x,y)(S_1igcup S_2)中相邻)可能对答案产生贡献,于是记下二元组((x,y))。根据启发式合并的性质知道这样的二元组有(O(nlg n))个。找出这些二元组之后扫描线,一样需要用到上面那棵线段树,时间相同。


using namespace std;
#include <bits/stdc++.h>
#define N 100005
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
#define ll long long
int n,Q;
char str[N];
int m,fa[N*2],id[N];
vector<pair<int,int> > q[N];
namespace SAM{
	struct Node{
		Node *c[26],*fail;
		int len;
	} d[N*2],*S,*T;
	void insert(int ch){
		Node *nw=&d[++m],*p;
		nw->len=T->len+1;
		for (p=T;p && !p->c[ch];p=p->fail)
			p->c[ch]=nw;
		if (!p)
			nw->fail=S;
		else{
			Node *q=p->c[ch];
			if (p->len+1==q->len)
				nw->fail=q;
			else{
				Node *clone=&d[++m];
				memcpy(clone,q,sizeof(Node));
				clone->len=p->len+1;
				for (;p && p->c[ch]==q;p=p->fail)
					p->c[ch]=clone;
				nw->fail=q->fail=clone;
			}
		}
		T=nw;
	}
	void build(){
		S=T=&d[++m];
		for (int i=1;i<=n;++i)
			insert(str[i]-'a'),id[i]=T-d;
		for (int i=2;i<=m;++i)
			fa[i]=d[i].fail-d;
	}
}
struct Node{
	Node *fa,*c[2];
	int isr;
	bool getson(){return fa->c[0]!=this;}
	void rotate(){
		Node *y=fa,*z=y->fa;
		if (y->isr!=-1)
			isr=y->isr,y->isr=-1;
		else
			z->c[y->getson()]=this;
		int k=getson();
		fa=z;
		y->c[k]=c[k^1],c[k^1]->fa=y;
		c[k^1]=y,y->fa=this;
	}
	void splay(){
		for (;isr==-1;rotate())
			if (!fa->isr)
				getson()!=fa->getson()?rotate():fa->rotate();
	}
} d[N*2],*null,*rt;
namespace SGT{
	int tag[N*4],mx[N*4];
	void modify(int st,int en,int c,int k=1,int l=1,int r=n){
		if (c<=tag[k])
			return;
		if (st<=l && r<=en){
			tag[k]=c;
			mx[k]=max(max(mx[k<<1],mx[k<<1|1]),tag[k]-l);
			return;
		}
		int mid=l+r>>1;
		if (st<=mid) modify(st,en,c,k<<1,l,mid);
		if (mid<en) modify(st,en,c,k<<1|1,mid+1,r);
		mx[k]=max(max(mx[k<<1],mx[k<<1|1]),tag[k]-l);
	}
	int query(int st,int en,int k=1,int l=1,int r=n){
		if (st<=l && r<=en)
			return mx[k];
		int mid=l+r>>1,res=tag[k]-max(l,st);
		if (st<=mid) res=max(res,query(st,en,k<<1,l,mid));
		if (mid<en) res=max(res,query(st,en,k<<1|1,mid+1,r));
		return res;
	}
}
void modify(int i){
	Node *x=&d[id[i]],*y=null;
	for (;x!=rt;y=x,x=x->fa){
		x->splay();
		if (x->isr){
			int l=SAM::d[x->fa-d].len+1,r=SAM::d[x-d].len;
//			printf("%d %d %d
",x->isr-r+1,x->isr-l+1,x->isr);
			SGT::modify(x->isr-r+1,x->isr-l+1,x->isr+1);
		}
		x->c[1]->isr=x->isr;
		x->c[1]=y;
		y->isr=-1;
		x->isr=i;
	}
}
int ans[N];
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);	
	scanf("%d%d",&n,&Q);
	scanf("%s",str+1);
	for (int i=1;i<=Q;++i){
		int l,r;
		scanf("%d%d",&l,&r);
		q[r].push_back(mp(l,i));
	}
	SAM::build();
	null=d;
	*null={null,null,null,-1};
	for (int i=1;i<=m;++i)
		d[i]={&d[fa[i]],null,null,0};
	rt=&d[1];
	for (int i=1;i<=n;++i){
		modify(i);
		for (int j=0;j<q[i].size();++j){
			int l=q[i][j].fi;
			ans[q[i][j].se]=i-l+1-max(SGT::query(l,i),0);
		}
	}
	for (int i=1;i<=Q;++i)
		printf("%d
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14620160.html