以前一直用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; }
时间: 2024-12-19 21:57:20