UVA 12206 Stammering Aliens(基于哈希值的LCP算法)

来源:互联网 发布:哪个网络机顶盒好用 编辑:程序博客网 时间:2024/05/16 17:38

UVA 12206 Stammering Aliens(基于哈希值的LCP算法)

题意:给你一个字符串s和m,求出字符串中至少出现m次的最长子串.如果有多解,输出最长字符串的长度以及它出现的最大位置.

分析:其实本题可以用后缀数组来解.下面用哈希值来做.详见刘汝佳训练指南P225

       首先对于一个长为n的字符串,我们定义它的哈希值为(下面的x值是人为设定的一个值):

s[0]+s[1]*x+s[2]*x^2+…s[n-1]*x^(n-1)

那么它的每个后缀[i,n-1]的哈希值为H[i]:

s[i]+s[i+1]*x+…s[n-1]*x^(n-1-i)

由上可以得到递推关系:

H[n]=0;

H[i]=H[i+1]*x+s[i]

那么对于该串s中的任意一段的哈希值为hash[i,L]:

hash[i,L]=s[i]+s[i+1]*x+s[i+2]*x^2+…s[i+L-1]*x^(L-1)=H[i]-H[i+L]*x^L

(验证一下上面的公式,看看是不是.其实主要是明白一段连续字符串的哈希值如何计算就可以,其他的递推公式是为了加快我们计算的.)

       在程序中,我们二分答案,一一试探看看所有长度为l的子串中有没有出现m次的.我们只需要求出长度为l的所有子串的哈希值,然后将该哈希值数组排序,然后从小到大扫描,看看有没有同一个哈希值连续出现了m次的,如果有就表示长度l可以,并且记下该串最后出现的位置pos即可.

程序中我们用unsighed long long里保存哈希值,如果数据太大就自动溢出了,对于不同长度的串具有同一个哈希值的可能行很小很小.如果不放心还可以改变x的值再计算一次看看两者的哈希值是否相同,如果还是相同,那么出错的可能性就更小了.

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn = 40000+1000;typedef unsigned long long LL;LL h[maxn],x;//后缀哈希值char str[maxn];int n,m,max_pos;LL mi[maxn];//mi[i]的值是x的i次方的值struct node{    LL hash;//起点为pos且长len的串的哈希值    int pos;//起点位置    bool operator <(const node &b)const    {        return hash<b.hash ||(hash==b.hash && pos<b.pos);    }}nodes[maxn];bool check(int len){    for(int i=0;i+len-1<=n-1;i++)//共有n-len+1个长为len的连续子串    {        nodes[i].hash=h[i]-h[i+len]*mi[len];        nodes[i].pos=i;//起点位置    }    sort(nodes,nodes+n-len+1);    max_pos=-1;    int sum=0;    for(int i=0;i+len-1<=n-1;i++)    {        if(i==0 || nodes[i].hash==nodes[i-1].hash)        {            sum++;            if(sum>=m) max_pos=max(max_pos,nodes[i].pos);        }        else sum=1;    }    return max_pos>=0;}int main(){    x=123;    while(scanf("%d",&m)==1&&m)    {        scanf("%s",str);        n=strlen(str);        h[n]=0;        for(int i=n-1;i>=0;i--)            h[i]=h[i+1]*x+str[i]-'a';        mi[0]=1;        for(int i=1;i<=n;i++)            mi[i]=mi[i-1]*x;        if(!check(1))        {            printf("none\n");            continue;        }        int min=1,max=n;        while(min<max)        {            int mid=min+(max-min+1)/2;            if(check(mid)) min=mid;            else max=mid-1;        }        check(min);        printf("%d %d\n",min,max_pos);    }    return 0;}


0 0