zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    java格式化sql
    Java 实现对Sql语句解析
    java 解析四则混合运算表达式并计算结果
    SQL转Java代码小工具
    eclipse导入maven工程步骤
    xml文件的schema也是经过jdk编译器编译的,如果xsd没引入完整,而xml中又用到了这些标签,就会编译不通过啊。
    eclipse bug之'<>'operator is not allowed for source level below 1.7
    eclipse默认指定项目的编译器版本
    eclipse导入maven项目后依赖jar包更新问题->update project按钮
    maven也是apache下的项目
  • 原文地址:https://www.cnblogs.com/jz-597/p/14620160.html
Copyright © 2011-2022 走看看