【洛谷2852】[USACO06DEC] Milk Patterns G(重拾字尾陣列)
阿新 • • 發佈:2020-10-22
- 給定一個長度為\(n\)的序列。
- 求出最長的出現至少\(k\)次的子串的長度。
- \(n\le2\times10^4\)
\(Height\)陣列
考慮二分答案\(x\),則存在一個出現\(k\)次、長度為\(x\)的子串,等價於存在\(k\)個串的\(LCP\)長度大於等於\(x\)。
貪心地考慮,必然選擇排名連續的\(k\)個串。
根據\(SA\)的\(Height\)陣列的性質,\([l,r]\)的\(LCP\)長度就是\(\min_{i=l+1}^rHeight_i\)。
所以就是要判斷是否存在連續\(k-1\)個\(Height_i\)全都大於等於\(x\)。
程式碼:\(O(nlogn)\)
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 20000 #define V 1000000 using namespace std; int n,k,a[N+5]; class SuffxArray { private: int SA[N+5],H[N+5],p[N+5],rk[N+5],t[V+5]; I void Sort(CI S) { RI i;for(i=1;i<=S;++i) t[i]=0;for(i=1;i<=n;++i) ++t[rk[i]]; for(i=1;i<=S;++i) t[i]+=t[i-1];for(i=n;i;--i) SA[t[rk[p[i]]]--]=p[i]; } public: I void Init(CI n,int *a)//初始化 { RI i,j,k;for(i=1;i<=n;++i) rk[p[i]=i]=a[i]; RI t=0,S=V;for(Sort(S),k=1;t^n;k<<=1,S=t)//倍增預處理SA陣列 { for(i=1;i<=k;++i) p[i]=n-k+i;for(t=k,i=1;i<=n;++i) SA[i]>k&&(p[++t]=SA[i]-k);//按照第二維大小確定順序 for(Sort(S),i=1;i<=n;++i) p[i]=rk[i];for(rk[SA[1]]=t=1,i=2;i<=n;++i) (p[SA[i]]^p[SA[i-1]]||p[SA[i]+k]^p[SA[i-1]+k])&&++t,rk[SA[i]]=t;//求出新的排名 } for(k=0,i=1;i<=n;++i) rk[SA[i]]=i;for(i=1;i<=n;++i)//預處理Height陣列 { if(k&&--k,rk[i]==1) continue;j=SA[rk[i]-1];//延續上一次的答案 W(i+k<=n&&j+k<=n&&a[i+k]==a[j+k]) ++k;H[rk[i]]=k;//向外擴充套件 } } I bool Check(CI x) {RI i,t=1;for(i=1;i<=n&&t<k;++i) H[i]>=x?++t:(t=1);return t==k;}//驗證是否存在連續k-1位 }S; int main() { RI i;for(scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",a+i); S.Init(n,a);RI l=0,r=n,mid;W(l<r) S.Check(mid=l+r+1>>1)?l=mid:r=mid-1;//二分答案 return printf("%d\n",l),0; }