【二分答案】【分块答案】【字符串哈希】【set】bzoj2946 [Poi2000]公共串

我们二分/分块枚举答案x,暴力把除了最短的字符串以外的其他字符串的x长度子串哈希搞出来,分别扔到set里。

然后暴力枚举最短的字符串的x长度字串,查看是否在全部的set里出现过。

#include<cstdio>
#include<set>
#include<cstring>
#include<cmath>
using namespace std;
typedef unsigned long long ull;
set<ull>T[6];
const ull seed=29;
ull seeds[2001],ord[301];
int n,ls[6],minv=2147483647,mini,ans;
char s[6][2001];
ull BKDRhash(const char str[],const int &L,const int &R)
{
    ull res=0;
    for(int i=L;i<R;++i)
	  res=res*seed+ord[str[i]];
    return res;
}
void init()
{
	for(int i='a',j=1;i<='z';++i,++j)
	  ord[i]=(ull)j;
    seeds[0]=1;
    for(int i=1;i<=minv;++i)
      seeds[i]=seeds[i-1]*seed;
}
bool Find_In_All(const ull &x)
{
	for(int i=1;i<=n;++i)
	  if(i!=mini)
	    if(T[i].find(x)==T[i].end())
	      return 0;
	return 1;
}
bool check(const int &x)
{
	for(int i=1;i<=n;++i) if(i!=mini)
	  {
	  	T[i].clear();
	  	ull hs=BKDRhash(s[i],0,x);
	  	T[i].insert(hs);
	  	for(int j=x;j<ls[i];++j)
	  	  {
	  	  	hs=hs*seed+ord[s[i][j]];
	  	    hs-=seeds[x]*ord[s[i][j-x]];
	  	    T[i].insert(hs);
	  	  }
	  }
	ull hs=BKDRhash(s[mini],0,x);
	if(Find_In_All(hs)) return 1;
	for(int i=x;i<minv;++i)
	  {
	  	hs=hs*seed+ord[s[mini][i]];
	  	hs-=seeds[x]*ord[s[mini][i-x]];
	    if(Find_In_All(hs)) return 1;
	  }
	return 0;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	  {
	  	scanf("%s",s[i]);
	  	ls[i]=strlen(s[i]);
	  	if(ls[i]<minv)
	  	  {
	  	  	minv=ls[i];
	  	  	mini=i;
	  	  }
	  }
	init();
	int sz=sqrt(minv),last=minv+1;
    for(int i=minv;last;i-=sz)
      {
        if(check(i))
          for(int j=last-1;j>=i;--j)
            if(check(j))
              {
                printf("%d
",j);
                return 0;
              }
        last=i;
      }
}
原文地址:https://www.cnblogs.com/autsky-jadek/p/4198147.html