给一个m和一个字符串,求最长的至少重复出现m次的子串。拿来练练字符串hash了,随然总感觉这种不处理冲突纯拼rp的方法有点不太科学...
做法的话跟后缀数组差不多了,还是二分长度l,去找串中每一个长度为l的子串看出现的次数是否大于等于m,复杂度NlogN.
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=50000;
const int x=123;
ull hash[maxn];
ull H[maxn];
ull xp[maxn];
char s[maxn];
int rank[maxn];
int n,m,p,q,k;
inline int idx(char c)
{
return c-'a';
}
void hashinit()
{
H[n]=0;
for (int i=n-1; i>=0; i--)
{
H[i]=H[i+1]*x+idx(s[i]);
}
}
int cmp(const int &a,const int &b)
{
return (hash[a]<hash[b] || (hash[a]==hash[b] && a<b ));
}
int pos;
int check(int l)
{
for (int i=0; i<n-l+1; i++)
{
rank[i]=i;
hash[i]=H[i]-H[i+l]*xp[l];
}
sort(rank,rank+n-l+1,cmp);//hash第i小的字符串的起点位置
int c=0;
pos=-1;
for (int i=0; i<n-l+1; i++)
{
if (i==0 || hash[rank[i]]!=hash[rank[i-1]]) c=0;
if (++c>=m) pos=max(pos,rank[i]);
}
return pos>=0;
}
int main()
{
// freopen("in.txt","r",stdin);
xp[0]=1;
for (int i=1; i<maxn; i++)
{
xp[i]=xp[i-1]*x;
}
while(~scanf("%d",&m) && m)
{
scanf("%s",s);
n=strlen(s);
hashinit();
int l=1,r=n+1;
int mid;
int ans;
if (check(1)==0)
{
puts("none");
continue;
}
ans=1;
while(l<r)
{
mid=(l+r)>>1;
if (check(mid)) ans=mid,l=mid+1;
else r=mid;
}
check(ans);
printf("%d %d\n",ans,pos);
}
return 0;
}