题目链接:http://poj.org/problem?id=3261
题意:求一个序列中,出现超过k次的子序列的最长长度
思路:我做的第一道hash题目,方法就是用二分来逼近最长长度,用hash来计算子序列出现的次数。hash水题,要注意的就是取模,还有二分的边界条件。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20000+10;
int arr[N];
typedef unsigned long long ll;
const int Mod=1e6+7;
ll xp[N],hash[N],hash2[N];//unsigned long long可以自动取模
int n,k;
bool check(int len)
{
int i,j;
for(i=0;i<n-(len-1);i++)//去长度为len的hash值
{
hash2[i]=hash[i]-hash[i+len]*xp[len];
}
sort(hash2,hash2+n-(len-1));
int maxx=0,cnt=1;
for(i=1;i<n-(len-1);i++)
{
if(hash2[i]==hash2[i-1]) cnt++;
else cnt=1;
if(cnt>maxx)//找最长的长度
maxx=cnt;
}
return maxx>=k;
}
int main ()
{
//freopen("in.txt","r",stdin);
int i,j;
xp[0]=1;
for(i=1;i<N;i++)
xp[i]=xp[i-1]*Mod;
while(~scanf("%d %d",&n,&k))
{
for(i=0;i<n;i++)
scanf("%d",&arr[i]);
hash[n]=0;
for(i=n-1;i>=0;i--)//取hash值
hash[i]=hash[i+1]*Mod+arr[i];
int l=1,r=n;
while(l<r)//二分,边界条件比较坑,好像一定要一个加,一个减否则停不下来或者错
{
int mid=(l+r)/2+1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
return 0;
}