思路讲解
我们来看看哈希怎么做
参考题解 https://www.luogu.com.cn/article/qpf5y3pt
那么其实最难的就是子串哈希的计算
哈希的递推公式是
h a s h [ i ] = s [ i ] + b a s e ∗ h a s h [ i − 1 ] hash[i]=s[i]+base*hash[i-1]
h a s h [ i ] = s [ i ] + b a s e ∗ h a s h [ i − 1 ]
由此可得
h a s h [ r ] = ( s [ r ] + s [ r − 1 ] ∗ b a s e + ⋯ + s [ l − 1 ] ∗ b a s e r − l + 1 + ⋯ + s [ 0 ] ∗ b a s e r ) hash[r]=(s[r]+s[r-1]*base+\cdots+s[l-1]*base^{r-l+1}+\cdots+s[0]*base^{r})
h a s h [ r ] = ( s [ r ] + s [ r − 1 ] ∗ b a s e + ⋯ + s [ l − 1 ] ∗ b a s e r − l + 1 + ⋯ + s [ 0 ] ∗ b a s e r )
h a s h [ l − 1 ] = ( s [ l − 1 ] + s [ l − 2 ] ∗ b a s e + ⋯ + s [ 0 ] ∗ b a s e l − 1 ) hash[l-1]=(s[l-1]+s[l-2]*base+\cdots+s[0]*base^{l-1})
h a s h [ l − 1 ] = ( s [ l − 1 ] + s [ l − 2 ] ∗ b a s e + ⋯ + s [ 0 ] ∗ b a s e l − 1 )
h a s h [ l : r ] = ( s [ r ] + s [ r − 1 ] ∗ b a s e + ⋯ + s [ l ] ∗ b a s e r − l ) hash[l:r]=(s[r]+s[r-1]*base+\cdots+s[l]*base^{r-l})
h a s h [ l : r ] = ( s [ r ] + s [ r − 1 ] ∗ b a s e + ⋯ + s [ l ] ∗ b a s e r − l )
可以推得下式(其实可以这么理解,把 h a s h [ l − 1 ] hash[l-1] h a s h [ l − 1 ] 左移 r − l + 1 r-l+1 r − l + 1 位)
h a s h [ l : r ] = h a s h [ r ] − h a s h [ l − 1 ] ∗ b a s e r − l + 1 hash[l:r]=hash[r]-hash[l-1]*base^{r-l+1}
h a s h [ l : r ] = h a s h [ r ] − h a s h [ l − 1 ] ∗ b a s e r − l + 1
至于height数组?直接用定义求,不搞那么多花里胡哨的
当然哈希还是慢了一点
下面是快排倍增做法
后缀的问题我们已经解决了,height数组是什么?
就是排序好的后缀,当前与上一个相同的前缀最长为多少(如上图所示)
那怎么求那?
运用下面这个定理
AC代码
AC https://www.luogu.com.cn/record/198571599
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 #include <iostream> #include <cstring> #include <algorithm> #include <deque> #include <queue> #include <vector> #include <set> #include <map> #include <unordered_map> #include <cmath> #include <bitset> #include <iterator> #include <random> #include <iomanip> #include <cctype> #include <array> using namespace std;typedef long long ll;typedef pair<ll,ll> pll;typedef array<ll,3> arr;const ll MAXN=static_cast <ll>(1e6 )+10 ;const ll base=131 ;ll N,T,hashe[MAXN],sa[MAXN],height[MAXN],powBase[MAXN]; string S; inline ll get_hash (ll a,ll b) { return hashe[b]-(a>0 ?hashe[a-1 ]:0 )*powBase[b-a+1 ]; } inline bool comp (ll a,ll b) { ll l=0 ,r=min (N-a,N-b); while (l<r) { ll mid=l+r+1 >>1 ; if (get_hash (a,a+mid-1 )==get_hash (b,b+mid-1 )){ l=mid; }else { r=mid-1 ; } } if (a+l<N && b+l<N && S[a+l]!=S[b+l]){ return S[a+l]<S[b+l]; } return N-a<N-b; } inline void get_height () { for (int i=1 ;i<N;++i){ ll a=sa[i-1 ],b=sa[i]; ll l=0 ,r=min (N-a,N-b); while (l<r) { ll mid=l+r+1 >>1 ; if (get_hash (a, a+mid-1 )==get_hash (b, b+mid-1 )){ l=mid; }else { r=mid-1 ; } } height[i]=l; } } inline void init () { ll val=0 ; for (int i=0 ;i<N;++i){ val=base*val+S[i]; hashe[i]=val; sa[i]=i; } powBase[0 ]=1 ; for (int i=1 ;i<=N+5 ;++i){ powBase[i]=powBase[i-1 ]*base; } } int main () { ios::sync_with_stdio (false ); cin.tie (0 );cout.tie (0 ); cin>>S; N=S.size (); init (); sort (sa, sa+N, comp); for (int i=0 ;i<N;++i) cout<<sa[i]<<' ' ; cout<<'\n' ; get_height (); for (int i=0 ;i<N;++i) cout<<height[i]<<' ' ; cout<<'\n' ; }
AC 倍增法
https://www.luogu.com.cn/record/198379595
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 #include <iostream> #include <cstring> #include <algorithm> #include <deque> #include <queue> #include <vector> #include <set> #include <map> #include <unordered_map> #include <cmath> #include <bitset> #include <iterator> #include <random> #include <iomanip> #include <cctype> #include <array> using namespace std;typedef long long ll;typedef pair<ll,ll> pll;typedef array<ll,3> arr;const ll MAXN=static_cast <ll>(3e5 )+117 ;char S[MAXN];ll rk[MAXN],sa[MAXN],tmp[MAXN],height[MAXN]; ll N; int K;bool comp_sa (ll a,ll b) { if (rk[a]!=rk[b]){ return rk[a]<rk[b]; }else { int ra= a+K<=N ? rk[a+K]:-1 ; int rb= b+K<=N ? rk[b+K]:-1 ; return ra<rb; } } void cal_sa () { for (int i=0 ;i<=N;++i){ rk[i]=i<N ? S[i]:-1 ; sa[i]=i; } for (K=1 ;K<=N;K=K*2 ){ sort (sa, sa+N, comp_sa); tmp[sa[0 ]]=0 ; for (int i=0 ;i<N;++i){ tmp[sa[i+1 ]]=tmp[sa[i]]+(comp_sa (sa[i], sa[i+1 ]) ? 1 :0 ); } for (int i=0 ;i<N;++i){ rk[i]=tmp[i]; } } } void getHeight () { for (int i=0 ;i<N;++i){ rk[sa[i]]=i; } for (int i=0 ,k=0 ;i<N;++i){ if (rk[i]==0 ) continue ; if (k) k--; int j=sa[rk[i]-1 ]; while (i+k<N && j+k<N && S[i+k]==S[j+k]) k++; height[rk[i]]=k; } } int main () { ios::sync_with_stdio (false ); cin.tie (0 );cout.tie (0 ); cin>>S; N=strlen (S); cal_sa (); for (int i=0 ;i<N;++i){ cout<<sa[i]<<' ' ; } cout<<'\n' ; getHeight (); for (int i=0 ;i<N;++i){ cout<<height[i]<<' ' ; } cout<<'\n' ; return 0 ; }
心路历程(WA,TLE,MLE……)
WA
https://www.luogu.com.cn/record/198357917
字符串哈希WA了一次,主要原因是整型溢出
https://www.luogu.com.cn/record/198561313
解决方法竟然是不管?用整形溢出自然取整?我不理解,但我大受震撼