您的位置:首页 > 其它

BZOJ2946 [Poi2000]公共串 二分+hash

2017-12-30 22:09 344 查看
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。n<=5,maxlen<=2000.

预处理出每个串的哈希值

二分答案len,前4个串的所有长为len的子串全部用map存,再最后一个串跑,看是否在前面全部出现.

常数略大,但是数据比较小没问题

#include <bits/stdc++.h>
#define ull unsigned long long
#define clr(x,i) memset(x,i,sizeof(x))
using namespace std;
const int N=2005;
const ull seed=173;
char str[6]
;
int n,len[6],a[6]
;
ull h[6]
,pw
;
map<int,ull> mp[6];
inline ull geth(int k,int l,int r)
{
return h[k][r]-h[k][l-1]*pw[r-l+1];
}
bool check(int now)
{
for(int i=1;i<=n;i++)
mp[i].clear();
for(int i=1;i<n;i++)
{
for(int j=1;j+now-1<=len[i];j++){
ull ret=geth(i,j,j+now-1);
mp[i][ret]=1;
}
}
for(int i=1;i+now-1<=len
;i++)
{
ull x=geth(n,i,i+now-1);
bool flag=1;
for(int j=1;j<n;j++)
if(!mp[j][x])flag=0;
if(flag)return true;
}
return false;
}
void solve()
{
int l=1,r=len[1],mid,ans=0;
for(int i=1;i<=n;i++)r=max(r,len[i]);
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
ans=mid,l=mid+1;
else
r=mid-1;
}
printf("%d",ans);
}
int main()
{
scanf("%d",&n);
if(n==1){
printf("0");return 0;
}
for(int i=1;i<=n;i++){
scanf("%s",str[i]+1);len[i]=strlen(str[i]+1);
for(int j=1;j<=len[i];j++)
a[i][j]=str[i][j]-'a'+1;
}
pw[0]=1;
for(int i=1;i<=2000;i++)pw[i]=pw[i-1]*seed;
for(int i=1;i<=n;i++)
for(int j=1;j<=len[i];j++)
h[i][j]=h[i][j-1]*seed+a[i][j];
solve();
return 0;
}
这个好像是后缀自动机的模板题,以后来补。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: