BZOJ4779[Usaco2017 Open]Bovine Genomics

我首先首先先考虑了二分+hash,但可能是我的二分有点问题,一直tle了一个点,然后完全不知道怎么去改变,只能去USACO的官网上看,结果发现了更厉害的方法。

原英文题解:http://www.usaco.org/current/data/sol_cownomics_gold_open17.html

那么我按照我的理解讲一下,首先我们开始定义一个区间lef和righ,开始我们将其全部赋值为0,表示求出lef+1到righ这个区间各个牛的hash值(我是用rand(),不过这个看自己的心情随便赋值吧),然后看看有没有重复的,如果这个区间双方的牛互相没有相同的hash值那么就可以更新答案,同时将lef++,同时每头牛的hash值减去最前面一个字母的hash,然后继续往下做(意思就是如果当前符合答案,那么我们把这个区间的最前面一位去掉,看看是否有更优的答案),如果有相同的hash值,那么说明当前区间还不可以,所以righ++,并将每头牛的hash值加上后面一位的值进行更新。他利用的就是如果当前区间是可行的,那么把它当做后缀的区间也一定是可行的,所以一定会访问到这个可行区间。

分析了一下,它的hash值计算是从上一次的推出来的,而我之前hash是每次都重新计算一遍,所以。。。

对了判断hash有无,很方便的可以用set来。

下面是具体程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#define maxn 1009
#define mo 53894320
using namespace std;
typedef long long ll;
char ch[maxn];
int n,ans,m,t;
ll val[maxn],s1[maxn][maxn],s2[maxn][maxn],hash1[maxn],hash2[maxn];
int main()
{
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) 
  {
   scanf("%s",ch);
   for (int j=0;j<m;j++) s1[i][j]=ch[j]-65;
  }
  for (int i=1;i<=n;i++) 
  {
   scanf("%s",ch);
   for (int j=0;j<m;j++) s2[i][j]=ch[j]-65;
  }
  for (int i=0;i<m;i++) val[i]=rand()%mo;
  ans=m;t=n;
  int lef=0,righ=0;
  while (righ<m)
  {
       set<ll>p; 
       if (t==0) ans=min(ans,righ-lef);
       if (t>0)
       {
        t=0;
        for (int i=1;i<=n;i++) 
      {
       hash1[i]=(hash1[i]+s1[i][righ]*val[righ])%mo;
       p.insert(hash1[i]);
      }
      for (int i=1;i<=n;i++)
      {
       hash2[i]=(hash2[i]+s2[i][righ]*val[righ])%mo;
       if (p.count(hash2[i])) t++;
      }
      righ++;
     }
     else
     {
      t=0;
      for (int i=1;i<=n;i++) 
      {
       hash1[i]=((hash1[i]-(s1[i][lef]*val[lef]))%mo+mo)%mo;
       p.insert(hash1[i]);
      } 
      for (int i=1;i<=n;i++)
      {
       hash2[i]=((hash2[i]-s2[i][lef]*val[lef])%mo+mo)%mo;
       if (p.count(hash2[i])) t++;
      }
      lef++;
     }
  }
  printf("%d
",ans);
  return 0;
} 
原文地址:https://www.cnblogs.com/2014nhc/p/7737106.html