POJ - 3693 Maximum repetition substring(后缀数组+RMQ)
題目鏈接:點擊查看
題目大意:給出一個字符串,求出字符串中?重復次數最多的連續重復子串 ,如果有多個答案,輸出字典序最小的
題目分析:又是一個模板題,這里放一個大佬的博客,講的很清楚:
https://blog.csdn.net/queuelovestack/article/details/53035903
具體做法就是,兩層for循環,第一層枚舉子串的長度,第二層枚舉每個位置:0 , i , 2*i ... k*i,這樣時間復雜度為nlogn,因為如果重復次數大于一次,那么必定會有相鄰的兩個位置 i*k 和 i*k+i 的最長公共前綴大于 i ,此時可以計算出重復次數ans為rmq(i*k,i*k+i)/i+1,這里的加一是因為本身的一次,在計算前綴時會覆蓋掉,這里又出現了一個新的問題,如果子串的起點相對于之前枚舉的位置在左右有所平移怎么辦,因為向右計算的已經全部計算完畢了,我們只需要考慮向左是否還能擴大答案,因為在枚舉的長度 i 固定的情況下,此時最多向左平移?i 個單位了,因為如果超過了 i 的話,那么就完全可以歸為i*(k-1)與i*k的計算范圍內了,所以為了湊整,我們令起點向左平移i-ans%i,此時的起點為i*k-(i-ans%i),我們記為st,再次計算看rmq(st,st+i)是否大于等于ans%i即可,為了方便起見,因為后面的字符串已經連接起來了,所以我們將判斷條件更改為大于等于i就可以了,以此維護出現次數的最大值
以上為計算重復次數最多的連續重復子串的模板方法,下面說一下如何求字典序最小
此時我們已經獲得了出現次數為mmax的所有長度的子字符串了,因為sa數組是通過排名排序的,我們枚舉所有的sa數組,找到第一個滿足rmq(sa[i],sa[i]+length[j])>=(mmax-1)*length的下標就是答案了,這里的mmax減一的原因和上面一樣,也是因為在計算前綴的時候會少算字符串本身
代碼:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<set> #include<cmath> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;char str[N];int sa[N]; //SA數組,表示將S的n個后綴從小到大排序后把排好序的 //的后綴的開頭位置順次放入SA中 int t1[N],t2[N],c[N];int rk[N],height[N],len;int s[N];int st[N][20];void build_sa(int s[],int n,int m)//n為添加0后的總長 {int i,j,p,*x=t1,*y=t2;for(i=0;i<m;i++) c[i]=0;for(i=0;i<n;i++) c[x[i]=s[i]]++;for(i=1;i<m;i++) c[i]+=c[i-1];for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;for(j=1;j<=n;j<<=1) {p=0;for(i=n-j;i<n;i++) y[p++]=i;for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;for(i=0;i<m;i++) c[i]=0;for(i=0;i<n;i++) c[x[y[i]]]++;for(i=1;i<m;i++) c[i]+=c[i-1];for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];swap(x,y);p=1,x[sa[0]]=0;for(i=1;i<n;i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;if(p>=n) break;m=p;} }void get_height(int s[],int n)//n為添加0后的總長 {int i,j,k=0;for(i=0;i<=n;i++)rk[sa[i]]=i;for(i=0;i<n;i++) {if(k) k--;j=sa[rk[i]-1];while(s[i+k]==s[j+k]) k++;height[rk[i]]=k;} }void solve(int base=128) {build_sa(s,len+1,base);get_height(s,len); }void ST_build() {for(int i=1;i<=len;i++)st[i][0]=height[i];for(int i=1;i<=log2(len);i++)for(int j=1;j+(1<<i)-1<=len;j++)st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]); }int ST_query(int a,int b) {int l=rk[a],r=rk[b];if(l>r)swap(l,r);l++;int k=log2(r-l+1);return min(st[l][k],st[r-(1<<k)+1][k]); }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int kase=0;while(scanf("%s",str)!=EOF&&str[0]!='#'){len=strlen(str);for(int i=0;i<len;i++)s[i]=str[i]-'a'+1;s[len]=0;solve(30);ST_build();int mmax=0;vector<int>length;for(int i=1;i<=len;i++){for(int j=0;j+i<len;j+=i){int k=ST_query(j,j+i);int ans=k/i+1;int st=j-(i-k%i);if(ST_query(st,st+i)>=i)ans++;if(ans>mmax){mmax=ans;length.clear();length.push_back(i);}else if(ans==mmax){length.push_back(i);}}}for(int i=1;i<=len;i++)//枚舉sa數組 {for(int j=0;j<length.size();j++){if(ST_query(sa[i],sa[i]+length[j])>=length[j]*(mmax-1)){printf("Case %d: ",++kase);for(int k=sa[i];k<sa[i]+length[j]*mmax;k++)putchar(str[k]);putchar('\n');goto end;}}}end:;}return 0; }?
總結
以上是生活随笔為你收集整理的POJ - 3693 Maximum repetition substring(后缀数组+RMQ)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 1096D E
- 下一篇: SPOJ - DISUBSTR Dist