首页 > 代码库 > bzoj1692: [Usaco2007 Dec]队列变换(hash+二分求LCP)

bzoj1692: [Usaco2007 Dec]队列变换(hash+二分求LCP)

  以前一直用SA求LCP,今天学习了一波hash+二分求LCP的姿势,也是nlogn而且常数更小了.

  hash+二分可以logn比较两个后缀的字典序大小,求出LCP然后比较LCP后一个字符的字典序

技术分享
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define ull unsigned long long
using namespace std;
ull mul[100010],hs[100010],ophs[100010],L,R,l,r,mid,len,num,n;
char s[100010];
void read(ull &k)
{
    int f=1;k=0;char c=getchar();
    while(c<0||c>9)c==-&&(f=-1),c=getchar();
    while(c<=9&&c>=0)k=k*10+c-0,c=getchar();
    k*=f;
}
int lcp()
{
    l=1;r=R-L+1;
    if(hs[L+r-1]-hs[L-1]*mul[r]==ophs[R-r+1]-ophs[R+1]*mul[r])return r;r--;
    while(l<r)
    {
        mid=(l+r+1)>>1;
        int hs1=hs[L+mid-1]-hs[L-1]*mul[mid];
        int hs2=ophs[R-mid+1]-hs[R+1]*mul[mid];
        if(hs1!=hs2)r=mid-1;else l=mid;
    //    if(s[L+l]!=s[R-l])return l;
    }
    return l;
}
bool cmp()
{
    if(s[L]!=s[R])return s[L]>s[R];
    len=lcp();
    if(len==R-L+1)return 1;
    else return s[L+len]>s[R-len];
}
int main()
{
    read(n);
    for(int i=1;i<=n;i++)
    for(s[i]=getchar();s[i]<A||s[i]>Z;s[i]=getchar());
    mul[0]=1;
    for(int i=1;i<=n;i++)mul[i]=mul[i-1]*107;
    for(int i=1;i<=n;i++)hs[i]=hs[i-1]*107+(ull)s[i]-A;
    for(int i=n;i;i--)ophs[i]=ophs[i+1]*107+(ull)s[i]-A;
    L=1;R=n;num=1;
    for(int i=1;i<=n;i++,num++)
    {
        putchar(cmp()?s[R--]:s[L++]);
        if(num==80)printf("\n"),num=0;
    }
    return 0;
}
View Code

 

bzoj1692: [Usaco2007 Dec]队列变换(hash+二分求LCP)