[BZOJ2946] [Poi2000]公共串

题目描述

Description

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

Input

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

Output

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

Sample Input

3
abcb
bca
acbc

Sample Output

题目分析

二分出一个长度 然后搞一个二维map来存前n-1个串的每个长度的hash值 然后再枚举最后一个串所有mid长度的hash值 检测是否在n-1个串里都出现过即可

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
int n;
char str[7][2010];
int len[10];
int seed=26,p=1000000009;
long long po[2010];
void init()
{
    po[0]=1;
    for(int i=1;i<=2000;i++) po[i]=po[i-1]*seed%p;
}
int minn=10000;
map<int,int> m[7];
int check(int mid)
{
    for(int i=1;i<=n;i++) m[i].clear();
    long long tmp=0;
    for(int i=1;i<n;i++)
    {
        tmp=0;
        for(int j=1;j<=mid;j++)
            tmp=(tmp*seed+str[i][j]-'a')%p;
        for(int j=mid;j<=len[i];j++)
            m[i][tmp]=1,tmp=(((tmp-po[mid-1]*(str[i][j+1-mid]-'a')%p+p)%p)*seed+(str[i][j+1]-'a'))%p;
    }
    tmp=0;
    for(int i=1;i<=mid;i++) tmp=(tmp*seed+str[n][i]-'a')%p;
    for(int i=mid;i<=len[n];i++)
    {
        int flag=1;
        for(int j=1;j<n;j++) if(m[j][tmp]==0) flag=0;
        if(flag) return 1;
        tmp=(((tmp-po[mid-1]*(str[n][i+1-mid]-'a')%p+p)%p)*seed+(str[n][i+1]-'a'))%p;
    }
    return 0;
}
int main()
{
    scanf("%d",&n);
    init();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str[i]+1);
        len[i]=strlen(str[i]+1);
        minn=min(minn,len[i]);
    }
    if(n==1)
    {
        printf("%d",minn);
        return 0;
    }
    int l=0,r=minn+1,ans=0,mid;
    while(l<r)
    {
        mid=(l+r)>>1;
        if(check(mid)) ans=max(ans,mid),l=mid+1;
        else r=mid;
    }
    printf("%d",ans);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注