对于这些英文的题目,我们的首要任务是搞清楚题目的意思。题目大意:
说实话,我起初拿到这题的时候并不认为这题和哈希有半毛钱关系……
好吧,我们还是讲题目吧。(正经脸)
1、我们把依次读入的x转化成一个二进制数c[i],得到一个01矩阵a[n][k-1]。
2、我们可以对a数组进行前缀求和,得到c数组的前缀和数组sum[n][k-1]。
3、我们枚举i和j(i>j),判断sum[i][l]-sum[j][l](0<=l<=k-1)是否等于sum[[i][0]-sum[j][0],若等于,则ans=max(ans,i-j)。注意:是i-j,而不是i-j+1,因为sum数组是前缀和。
以上都是O(n^2)的正常思路,可惜n的最大值为100000,铁定TLE。
接下来我将详细阐述正解:hash
请观察上述解法第3步的判断,若sum[i]-sum[j]=sum[i][0]-sum[j][0],则sum[i][0]-sum[j][0]=sum[i][1]-sum[j][1]=……=sum[i][k-1]-sum[j][k-1]
由上述等式可得:sum[i][1]-sum[i][0]=sum[j][1]-sum[j][0],sum[i][2]-sum[i][0]=sum[j][2]-sum[j][0]……sum[i][k-1]-sum[i][0]=sum[j][k-1]-sum[j][0]
上述等式的变形是这道题的解题关键所在。经过上述对sum数组的操作后,我们可以得到一个新的数组:c[i][j]表示sum[i][j]-sum[i][0]
然后原问题就转化成了求c数组中相同两行的最远距离,设k=hash(c[i]),在枚举h[k]中的所有元素,更新ans。
小提示:记得在更新ans之前在h[0]中加入一个元素:0,即在c数组加上第0行:全都是0的一行。
附上AC代码:
#include <cstdio> #include <vector> #define lyf 233333 using namespace std; vector <int> h[lyf+1]; int n,m,x,a[100010][31],sum[100010][31],c[100010][31],ans; int abs(int a){ return a>0?a:-a; } int hash(int x){ int ans=0; for (int i=0; i<=m; ++i) ans+=c[x][i]*i; return abs(ans)%lyf; } bool pd(int i,int j){ for (int k=0; k<=m; ++k) if (c[i][k]!=c[j][k]) return 0; return 1; } int max(int a,int b){ return a>b?a:b; } int main(void){ scanf("%d%d",&n,&m);--m; h[0].push_back(0); for (int i=1; i<=n; ++i){ scanf("%d",&x); for (int j=0; j<=m; ++j){ a[i][j]=x%2,x/=2; sum[i][j]=sum[i-1][j]+a[i][j]; c[i][j]=sum[i][j]-sum[i][0]; } int k=hash(i); for (int j=0; j<h[k].size(); ++j) if (pd(i,h[k][j])) ans=max(ans,abs(i-h[k][j])); h[k].push_back(i); } printf("%d",ans); return 0; }