我首先首先先考虑了二分+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\n",ans); return 0; }