给一个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;
}