这道题感觉真的很好,做完之后对hash的理解也更加深刻了。自己当时没想到能用hash,后来看了官方解后才恍然大悟,不过真的很难想到这样转化啊
首先用sum[i][j]表示从第一行到第i行具有特性j的牛的头数,显然若满足 sum[i][t]-sum[j][t]=常数 (1<=t<=k),则从第i+1行到第j行的牛符合题目要求。但如果是这样表示的话,最后还是要枚举任意两行,那么这个处理也就没多大作用了。重点来了,我们可以令c[i][j]=sum[i][j]-sum[i][1](1<=j<=k) ,所以上等式等价于 c[i][t]=c[j][t](1<=t<=k), 这样的话,就可以应用hash了。我的做法是将每一行的c[i][j]值相加,作为其哈希值,若出现两行的hash值相等,再去比较这两行对应的c[i][j]是否都相等。很显然这样处理后,就避免了任意两行都去比较了,因此hash的好处得到了充分的体现!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
vector<int>hash[100001];
int sum[100001][31];//sum[i][j]记录从第一行到第i行第j个特性出现的次数
int c[100001][31];//c[i][j]=sum[i][j]-sum[i][0]
int n,k;
void to_bit(int *arr,int num)//十进制转化为二进制
{
int i=0;
while(num)
{
arr[++i]=(num&1);
num/=2;
}
for(i++;i<=k;i++)//不满的位填0
arr[i]=0;
}
int main()
{
int i,j,num,MAX,bit[31];
while(~scanf("%d%d",&n,&k))
{
for(i=1;i<=n;i++)//读入数据并计算sum[i][j]
{
scanf("%d",&num);
to_bit(bit,num);
for(j=1;j<=k;j++)
sum[i][j]=sum[i-1][j]+bit[j];
}
for(i=1;i<=n;i++)//计算c[i][j]
for(j=1;j<=k;j++)
c[i][j]=sum[i][j]-sum[i][1];
for(i=1;i<=n;i++)
hash[i].clear();
MAX=0;
for(i=0;i<=n;i++)//注意要从0开始
{
//哈希(求和再模n)
num=0;
for(int j=1;j<=k;j++)
num+=c[i][j];
if(num<0)
num=-num;
num%=n;
//对于哈希值相等的行,再判断该行对应的各元素是否都相等
for(j=0;j<hash[num].size();j++)
{
int t=hash[num][j],x=0;
while(x<=k&&c[i][x]==c[t][x])
x++;
if(x>k&&MAX<(i-t))//第i行和第t行对应相等,说明t+1~i满足题意(这也是为何i从0开始的原因)
MAX=i-t;
}
hash[num].push_back(i);
}
printf("%d\n",MAX);
}
return 0;
}