zoukankan      html  css  js  c++  java
  • 【BZOJ2946】[Poi2000]公共串 后缀数组+二分

    【BZOJ2946】[Poi2000]公共串

    Description

           给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
    任务:
    l        读入单词
    l        计算最长公共子串的长度
    l        输出结果

    Input

    文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。

    Output

    仅一行,一个整数,最长公共子串的长度。

    Sample Input

    3
    abcb
    bca
    acbc

    Sample Output

    题解:把所有字符串连起来,中间用极小值隔开(极小值不能相同,用最大值也行),跑后缀数组,然后二分答案,如果能找出一段区间包括所有的单词,并且公共线坠长度≥答案,则答案可行

    感觉几年没写后缀数组了,细节多得可以~

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,num;
    const int maxn=10100;
    int r[maxn],ra[maxn],rb[maxn],sa[maxn],sb[maxn],st[maxn],rank[maxn],h[maxn],s[10],bel[maxn];
    char str[maxn];
    void work()
    {
    	int i,j,k,*x=ra,*y=rb,p;
    	for(i=0;i<n;i++)	st[x[i]=r[i]]++;
    	for(i=1;i<m;i++)	st[i]+=st[i-1];
    	for(i=n-1;i>=0;i--)	sa[--st[x[i]]]=i;
    	for(j=p=1;p<n;j<<=1,m=p)
    	{
    		for(i=n-j,p=0;i<n;i++)	y[p++]=i;
    		for(i=0;i<n;i++)	if(sa[i]>=j)	y[p++]=sa[i]-j;
    		for(i=0;i<m;i++)	st[i]=0;
    		for(i=0;i<n;i++)	st[x[y[i]]]++;
    		for(i=1;i<m;i++)	st[i]+=st[i-1];
    		for(i=n-1;i>=0;i--)	sa[--st[x[y[i]]]]=y[i];
    		for(swap(x,y),i=p=1,x[sa[0]]=0;i<n;i++)
    			x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
    	}
    	for(i=1;i<n;i++)	rank[sa[i]]=i;
    	for(i=k=0;i<n-1;h[rank[i++]]=k)
    		for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    }
    int check(int sta)
    {
    	memset(s,0,sizeof(s));
    	int i,sum=0,l=0;
    	s[bel[sa[0]]]++,sum+=(s[bel[sa[0]]]==1);
    	for(i=1;i<n;i++)
    	{
    		s[bel[sa[i]]]++,sum+=(s[bel[sa[i]]]==1);
    		if(h[i]<sta)	for(;l<i;l++)	s[bel[sa[l]]]--,sum-=(s[bel[sa[l]]]==0);
    		else if(sum==num)	return 1;
    	}
    	return 0;
    }
    int main()
    {
    	scanf("%d",&num);
    	int i,j,a;
    	for(i=1;i<=num;i++)
    	{
    		scanf("%s",str);
    		a=strlen(str);
    		for(j=0;j<a;j++)	bel[n]=i,r[n++]=str[j]-'a'+num;
    		n++;
    	}
    	for(i=0;i<n;i++)	if(!bel[i])	r[i]=bel[i-1]-1;
    	m=26+num;
    	work();
    	int L=1,R=n/num,mid;
    	while(L<R)
    	{
    		mid=L+R>>1;
    		if(check(mid))	L=mid+1;
    		else	R=mid;
    	}
    	printf("%d
    ",L-1);
    	return 0;
    }
  • 相关阅读:
    Solaris安装pkg
    JSP路径出现问题
    错误卸载软件导致Windows7系统中的软件无法播放视频
    蛋疼的Solaris设置
    SQL运行突然SESSION中断错误
    JAVA利用Zip4j解压缩
    java解压缩/压缩/加密压缩/加密解压缩 ZIP4J---ZIP文件压缩与解压缩学习
    未得冠军的运动员也有教练——Leo鉴书71
    VS2008 编译SQLite 3.8.4.3 + sqlcipher-3.1.0 + openssl-1.0.1g
    Android数据库安全解决方案,使用SQLCipher进行加解密
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/6639143.html
Copyright © 2011-2022 走看看