目录
1.题目
2.思路
1.字典树(trie 树)
2.hash(正解)
1.有注释版
2. copy 版
3.后文
1.题目
Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation, he discovered that although he can't predict the quality of milk from one day to the next, there are some regular patterns in the daily milk quality.
To perform a rigorous study, he has invented a complex classification scheme by which each milk sample is recorded as an integer between 0 and 1,000,000 inclusive, and has recorded data from a single cow over N (1 ≤ N ≤ 20,000) days. He wishes to find the longest pattern of samples which repeats identically at least K (2 ≤ K ≤ N) times. This may include overlapping patterns -- 1 2 3 2 3 2 3 1 repeats 2 3 2 3 twice, for example.
Help Farmer John by finding the longest repeating subsequence in the sequence of samples. It is guaranteed that at least one subsequence is repeated at least K times.
(某朋友:不要说鸟语)
好的好的,说中文······
农夫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。
一句话描述一下:
在数组里找到重复了k次的最长字串。
2.思路
1.字典树(trie 树)
虽然当时是考Hash
但我毅然决然的使用了字典树······
(确实很像模板)
再加上hash(114514,1919810)个优化后
荣获70(疯狂TLE)······
还是友好的给一下源码吧······
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int N=2002000;
int son[N][27],a[N],n,k,mx,idx;
struct zmx{int l,ans;}cnt[N+3000];
inline int read()
{
int x=0,w=0;char c=0;
while(!isdigit(c)) {w|=c=='-';c=getchar();}
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return w?-x:x;
}
void insert(string str,int l)
{
int p=0;
for(register int i=0;str[i];i++)
{
int u=str[i]-'0';
if(!son[p][u]) son[p][u]=++idx;
p=son[p][u];
}
cnt[p].ans+=1;
cnt[p].l=l;
}
signed main()
{
n=read(),k=read();
for(register int i=1;i<=n;i++) a[i]=read();
for(register int i=1;i<=n;i++)
{
string s="";
for(register int j=i;j<=n;j++)
{
s+=char(a[j]+'0');
insert(s,j-i+1);
}
}
for(int i=1;i<N+3000;i++)
if(cnt[i].ans==k&&cnt[i].l>mx) mx=cnt[i].l;
cout<<mx;
return 0;
}
2.hash(正解)
首先,数据告诉你:是一个极限(?)
所以由此推出:一定是前缀Hash。
知道了这个,任务就解决了一半。
同样的:还告诉你:二分适合用一用。
所以另一半就是二分答案。
所以基本完了(真的吗???)
既然是最大值,就是upper_bound
对了,用双重Hash防止冲突。
所以Hash函数即为:
for(int i=1;i<=n;i++) h1[i]=h1[i-1]*p,h[i]=h[i-1]*p+a[i];
查找即为
return h[r]-h[l-1]*h1[r-l+1];
然后?没有然后了呗!(好水呀qwq)
Q:关于为用二分答案有单调性否?
A:
对于一个长度为n的串s,s加上某个字符组成一个长度为n+1的串t
t在原串中的出现次数<=s在原串中的出现次数即关于长度有单调性 可以二分答案长度
1.有注释版
#include<bits/stdc++.h>//前缀哈希+双重哈希
using namespace std;
#define int unsigned long long//小心而谨慎的开了ULL(当然,是60分把我搞蒙了乱开的)
const int p=13331;//没错,60分的真正原因是这个
const int N=100010;//数组大小
int a[N],n,k,ans,h1[N]={1},h[N];//oh,定义
map<int,int>mp;//记录某长度的字串的出现次数
int Hash(int l,int r) {return h[r]-h[l-1]*h1[r-l+1];} //美妙的hash
bool check(int mid)//二分答案
{
int ans=0;
for(int r=mid;r<=n;r++)
{
mp[Hash(r-mid+1,r)]++;//I love map!!!
ans=max(ans,mp[Hash(r-mid+1,r)]);//看一看出现的所有长度为mid的串统计个数
//看看有没有一种子串出现>=k次即可
}
return ans>=k;//划分
}
signed main()
{
scanf("%u%u",&n,&k);//读入
for(int i=1;i<=n;i++) scanf("%u",&a[i]);//读入
for(int i=1;i<=n;i++) h1[i]=h1[i-1]*p,h[i]=h[i-1]*p+a[i];//Hash
int l=0,r=n+1;//l&&r
while(l+1<r)//二分答案
{
int mid=(l+r)/2;
if(check(mid)) l=mid;//二分
else r=mid;//二分
}
printf("%u",l);//输出
return 0;
}
2. copy 版
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int p=13331;
const int N=100010;
int a[N],n,k,ans,h1[N]={1},h[N];
map<int,int>mp;
int Hash(int l,int r) {return h[r]-h[l-1]*h1[r-l+1];}
bool check(int mid)
{
int ans=0;
for(int r=mid;r<=n;r++)
{
mp[Hash(r-mid+1,r)]++;
ans=max(ans,mp[Hash(r-mid+1,r)]);
}
return ans>=k;
}
signed main()
{
scanf("%u%u",&n,&k);
for(int i=1;i<=n;i++) scanf("%u",&a[i]);
for(int i=1;i<=n;i++) h1[i]=h1[i-1]*p,h[i]=h[i-1]*p+a[i];
int l=0,r=n+1;
while(l+1<r)
{
int mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%u",l);
return 0;
}
3.后文
最后?
点个赞呗!!!