[題解][筆記]字尾陣列&重複旋律1
阿新 • • 發佈:2020-10-29
[題解][筆記]字尾陣列&重複旋律1
暫時先引用YYL神佬的部落格
注意
這篇部落格比較沒用,主要是記錄一下思路
先放程式碼
#include <bits/stdc++.h> using namespace std; struct Rank{ int key1,key2,id; }ss[1000050]; int rk[1000050],sa[1000050],height[1000050],tmp[1000050],n,k,rkk; bool cmp(Rank x,Rank y){ if(x.key1 == y.key1)return x.key2 < y.key2; return x.key1 < y.key1; } bool check(int mid){ int cnt = 1,size = 1; for(int i = 2;i <= n;i++){ if(height[i] < mid)cnt = max(cnt,size),size = 1;//統計是否有連續的m-1個height陣列中的元素大於mid else size++; } return cnt >= k; } void get_height(){ int k = 0; for(int i = 1;i <= n;i++)rk[sa[i]] = i; for(int i = 1;i <= n;i++){//算height if(k)k--;//因為height[i]≥height[i-1]-1,所以不用從0開始,否則效率會大大降低 int j = sa[rk[i] - 1]; while(tmp[i + k] == tmp[j + k])//暴力匹配 k++; height[rk[i]] = k; } } void init_rank(){ rkk = 1; rk[ss[1].id] = 1; for(int i = 2;i <= n;i++){ if(ss[i].key1 != ss[i - 1].key1 || ss[i - 1].key2 != ss[i].key2) rkk++; rk[ss[i].id] = rkk; } } void get_rank(){ sort(ss + 1,ss + n + 1,cmp); init_rank(); int x = 1; for(int i = 1;i <= n;i <<= 1){//倍增求rank for(int j = 1;j <= n;j++){ ss[j].key1 = rk[j];ss[j].id = j; if(i + j > n) ss[j].key2 = 0; else ss[j].key2 = rk[j + i]; } sort(ss + 1,ss + n + 1,cmp); init_rank(); if(rkk == n)//所有元素都被排好序了就可以退出了 break; } } int main(){ scanf("%d%d",&n,&k); for(int i = 1;i <= n;i++){ scanf("%d",&ss[i].key1); ss[i].key2 = ss[i].key1; tmp[i] = ss[i].key1; ss[i].id = i; } get_rank(); for(int i = 1;i <= n;i++)sa[rk[i]] = i; get_height(); int l = 1,r = n,ans = 0; while(l <= r){ int mid = (l + r) / 2; if(check(mid)) ans = mid,l = mid + 1; else r = mid - 1; } printf("%d\n",ans); return 0; }
大部分程式碼都有註釋,現在就重點講一下二分求答案的部分
首先我們知道這個二分統計的是$height$陣列中大小大於$mid$的元素是否有$k-1$個,$mid$是直接二分的答案,也就是長度,那為什麼可以這麼二分呢?因為$height$陣列是相鄰字尾的最長相同字首,又因為是兩兩比較,所以是和$k-1$比較,通過上文對$height$陣列的講解,其實我們可以發現,這個二分的過程就是求出第$x$個字尾和第$x+k-1$個字尾的最長相同字首,那麼這個字首就是在字串中出現了$k$次的滿足條件的答案.