zoukankan      html  css  js  c++  java
  • 【题解】SP1811 LCS

    (color{purple}{Link})

    ( ext{Solution:})

    题目要求找到两个串的最长公共子串。(LCP)

    我们将两个串中间和末尾插入终止符,并弄到一棵后缀树上去。

    然后我们发现,对于一个叶子节点,它属于哪个子串,我们只需要找到它的父边上第一个出现的终止符属于哪个边即可。

    这里,我们可以用个奇技淫巧——前缀和实现。

    介于( ext{Suffix Tree})的边都是压缩的,所以维护信息变得不是很容易,所以可以采用一个在插入外面进行预处理前缀和的方式维护。

    然后,我们只需要找到一个最深的非叶子节点,使得它的子树中既含有第一个串串的终止符,也含有第二个串串的终止符即可。

    此时它的答案就是这个点的深度。

    ( ext{Suffix Tree})的主要问题就在于边上信息的维护。如果找不到一个好的方法去维护,( ext{Suffix Tree})还是很麻烦的。

    最近做题,题解区域都没有( ext{Suffix Tree})的题解,做起来真的挺累……我太菜了。

    #include<bits/stdc++.h>
    #include<ctime>
    using namespace std;
    const int MAXN=1.2e6+10;
    string Z,z;
    int n,val[MAXN],ans,tot;
    int sum[MAXN],sum2[MAXN];
    const int inf=(1<<30);
    struct SuffixTree {
    	int link[MAXN],ch[MAXN][28],now,rem,n;
    	int start[MAXN],len[MAXN],tail,s[MAXN];
    	SuffixTree() {
    		tail=now=1;
    		rem=n=0;
    		len[0]=inf;
    	}
    	inline int build(int a,int b) {
    		link[++tail]=1;
    		start[tail]=a;
    		len[tail]=b;
    		return tail;
    	}
    	void Extend(int x) {
    		s[++n]=x;
    		++rem;
    		for(int last=1; rem;) {
    			while(rem>len[ch[now][s[n-rem+1]]])
    				rem-=len[now=ch[now][s[n-rem+1]]];
    			int &v=ch[now][s[n-rem+1]];
    			int c=s[start[v]+rem-1];
    			if(!v||x==c) {
    				link[last]=now;
    				last=now;
    				if(!v)v=build(n,inf);
    				else break;
    			} else {
    				int u=build(start[v],rem-1);
    				ch[u][c]=v;
    				ch[u][x]=build(n,inf);
    				start[v]+=rem-1;
    				len[v]-=rem-1;
    				link[last]=v=u;
    				last=u;
    			}
    			if(now==1)--rem;
    			else now=link[now];
    		}
    	}
    } T;
    void predfs(int u,int dep) {
    	if(dep>=inf) {
    		int L=T.start[u];
    		int R=L+T.len[u]-1;
    		R=min(R,T.n);
    		int V=sum[R]-sum[L-1];
    		if(V)val[u]=1;
    		else{
    			V=sum2[R]-sum2[L-1];
    			if(V)val[u]=2;
    		}
    		return;
    	}
    	for(int i=0; i<28; ++i) {
    		if(T.ch[u][i]) {
    			predfs(T.ch[u][i],dep+T.len[T.ch[u][i]]);
    			val[u]|=val[T.ch[u][i]];
    		}
    	}
    	if(val[u]>=3)ans=max(ans,dep);
    }
    char buf[1<<21],*p1=buf,*p2=buf;
    string read(){
    	#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    	string s="";
    	char ch=gc();
    	while(ch=='
    ')ch=gc();
    	while(ch!='
    ')s+=ch,ch=gc();
    	return s;
    }
    int main() {
    //	freopen("1.in","r",stdin);
    //	freopen("SP.out","w",stdout);
    	clock_t ST,ET;
    	ST=clock();
    	z=read();Z=read();
    	z+=(char)'a'+26;
    	z+=Z;z+=(char)'a'+27 ;
    	for(int i=0;i<z.size();++i)z[i]-='a',T.Extend(z[i]);
    	tot=z.size();
    	for(int i=1; i<=tot; ++i) {
    		sum[i]=sum[i-1]+(T.s[i]==26);
    		sum2[i]=sum2[i-1]+(T.s[i]==27);
    	}
    	predfs(1,0);
    	printf("%d
    ",ans);
    	ET=clock();
    //	cout<<(double)(ET-ST)/CLOCKS_PER_SEC<<"s"<<endl;
    	return 0;
    }
    

    注意,如果在(dfs)里面来根据这条边的起点和终点暴力处理的话,这就是个(n^2)暴力。观察到,我们只需要在叶子的节点处理,而在叶子节点暴力处理的复杂度也不够优秀。

    观察到,第一个字符串的终止符一定先于第二个字符串的终止符出现(如果有的话)。那么,根据前缀和,先判断第一个终止符,再判断第二个终止符即可。

    最后时间复杂度是:

    ( ext{We let D show the constant,then the complexity is O(D*N).N is the length of these strings.})

  • 相关阅读:
    SharePoint 2013 配置基于表单的身份认证
    SharePoint 2013 场解决方案包含第三方程序集
    SharePoint 2010 站点附加数据升级到SP2013
    SharePoint 2013 在母版页中插入WebPart
    SharePoint 2013 搭建负载均衡(NLB)
    SharePoint 部署解决方案Feature ID冲突
    SharePoint 2013 配置基于AD的Form认证
    SharePoint Server 2016 Update
    SharePoint 2013 为用户组自定义EventReceiver
    SharePoint 2013 JavaScript API 记录
  • 原文地址:https://www.cnblogs.com/h-lka/p/13194066.html
Copyright © 2011-2022 走看看