题目
农夫John发现他的奶牛产奶的质量一直在变动。经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠。我们称之为一个“模式”。 John的牛奶按质量可以被赋予一个0到1000000之间的数。并且John记录了N(1<=N<=20000)天的牛奶质量值。他想知道最长的出现了至少K(2<=K<=N)次的模式的长度。比如1 2 3 2 3 2 3 1 中 2 3 2 3出现了两次。当K=2时,这个长度为4。
题解1
后缀数组
后缀数组是个好东西,可惜我不懂。
看题时,确实是一道求重复次数不小于k的最长子串的问题,是“后缀数组的裸题”?。
题解2
hash
Hash也可以?只要有字符串是否相同的问题,hash就可以发挥它的作用。
二分模式的长度len。判断时,把长度为len的模式的hash值全部存入h数组进行排序,其作用是计算有多少个同hash值相同的模式(即重复的模式),不小于k则返回true。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const int maxn=20010;
const ull P=131;ull power[maxn];
int n,k;
int a[maxn];
ull hash[maxn];
ull H(int l,int r)
{
return hash[r]-hash[l-1]*power[r-(l-1)];
}
ull h[maxn];
bool check(int len)//判断是否有重复不小于k次的模式
{
for(int r=len;r<=n;r++)
{
h[r]=H(r-len+1,r);
}
sort(h+len,h+n+1);
int cnt=1;//统计重复次数
for(int i=len+1;i<=n;i++)
{
if(h[i]==h[i-1])
{
cnt++;
if(cnt>=k) return true;
}
else cnt=1;
}
return false;
}
int main()
{
scanf("%d%d",&n,&k);
power[0]=1;hash[0]=0;
for(int i=1;i<=n;i++)
{
power[i]=power[i-1]*P;
scanf("%d",&a[i]);
hash[i]=hash[i-1]*P+a[i];
}
int l=1,r=n,ans;
while(l<=r)
{
int mid=l+r>>1;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}