• bzoj 2946 [Poi2000]公共串


    LINK:公共串

    给定n个串 求最长公共子串的长度。

    可以广义SAM 求出类似于right集的表示分属某个串的东西可以直接暴力跳 当然这里n只有5 所以可以状压一下用按位或 来做 最后扫一下所有节点就行了。

    但我打算使用SA来做 串在一起求SA 经典做法是二分 因为很难找到答案。

    但是分析性质 可以发现最长的子串连在一起的串肯定少 我们可以利用尺取法来做。

    分析答案的位置 不难发现尺取法刚好可以找到答案。

    const int MAXN=20010;
    int n,m,cnt,sum,ans,l,r;
    int h[MAXN],s[MAXN],vis[MAXN],w[MAXN];
    int pos[MAXN],x[MAXN],y[MAXN],c[MAXN],sa[MAXN],rk[MAXN];
    char a[MAXN],b[MAXN];
    inline void SA()
    {
    	m=150;
    	rep(1,cnt,i)++c[x[i]=a[i]];
    	rep(1,m,i)c[i]+=c[i-1];
    	fep(cnt,1,i)sa[c[x[i]]--]=i;
    	for(int k=1;k<=cnt;k=k<<1)
    	{
    		int num=0;
    		rep(cnt-k+1,cnt,i)y[++num]=i;
    		rep(1,cnt,i)if(sa[i]>k)y[++num]=sa[i]-k;
    		rep(1,m,i)c[i]=0;
    		rep(1,cnt,i)++c[x[i]];
    		rep(1,m,i)c[i]+=c[i-1];
    		fep(cnt,1,i)sa[c[x[y[i]]]--]=y[i];
    		rep(1,cnt,i)y[i]=x[i],x[i]=0;
    		x[sa[1]]=num=1;
    		rep(2,cnt,i)x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?num:++num;
    		if(num==cnt)break;
    		m=num;
    	}
    	//rep(1,cnt,i)printf("%d ",sa[i]);
    	rep(1,cnt,i)rk[sa[i]]=i;
    }
    inline void get_H()
    {
    	int k=0;
    	rep(1,cnt,i)
    	{
    		if(rk[i]==1)continue;
    		if(k)--k;//h[i]>=h[i-1]-1
    		int j=sa[rk[i]-1];
    		while(a[i+k]==a[j+k])++k;
    		h[rk[i]]=k;
    	}
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	gt(n);
    	rep(1,n,i)
    	{
    		scanf("%s",b+1);
    		int len=strlen(b+1);
    		rep(1,len,j)a[++cnt]=b[j],pos[cnt]=i;
    		a[++cnt]='z'+1;
    	}
    	a[cnt]=0;--cnt;
    	if(n==1){printf("%d
    ",cnt);return 0;}
    	SA();get_H();
    	//rep(1,cnt,i)put(rk[i]);
    	int L=1,R=1;l=1;
    	while(R<=cnt)
    	{
    		if(sum<n)
    		{
    			++R;int x=sa[R],y=sa[R-1];
    			if(pos[x])++vis[pos[x]],sum+=(vis[pos[x]]==1);
    			if(pos[y])++vis[pos[y]],sum+=(vis[pos[y]]==1);
    			while(l<=r&&h[R]<=s[r])--r;
    			s[++r]=h[R];w[r]=R;
    		}
    		while(sum==n)
    		{
    			ans=max(ans,s[l]);++L;
    			int x=sa[L],y=sa[L-1];
    			if(pos[x])--vis[pos[x]],sum-=(!vis[pos[x]]);
    			if(pos[y])--vis[pos[y]],sum-=(!vis[pos[y]]);
    			while(w[l]<=L&&l<=r)++l;
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    坑点:最后一个位置没用 但是必要的是清0 n可能等于1 后缀数组可能手残打错 单调队列两个条件都要加上。

  • 相关阅读:
    【转】HashMap、TreeMap、Hashtable、HashSet和ConcurrentHashMap区别
    【转】ArrayList循环遍历并删除元素的常见陷阱
    【转】Java内存管理:深入Java内存区域
    【转】java-String中的 intern()
    Jenkins + Ant + Git + Tomcat自动化部署
    Java的四种内部类
    java中的匿名内部类总结
    【转】如何提高意志力&如何坚持每天学习
    【转】前端工程筹建NodeJs+gulp+bower
    转 旧衣服不要扔,竟然还能这样改造,美翻了!
  • 原文地址:https://www.cnblogs.com/chdy/p/12588833.html
Copyright © 2020-2023  润新知