POJ - 3693 Maximum repetition substring(后缀数组+RMQ)
題目鏈接:點(diǎn)擊查看
題目大意:給出一個(gè)字符串,求出字符串中?重復(fù)次數(shù)最多的連續(xù)重復(fù)子串 ,如果有多個(gè)答案,輸出字典序最小的
題目分析:又是一個(gè)模板題,這里放一個(gè)大佬的博客,講的很清楚:
https://blog.csdn.net/queuelovestack/article/details/53035903
具體做法就是,兩層for循環(huán),第一層枚舉子串的長(zhǎng)度,第二層枚舉每個(gè)位置:0 , i , 2*i ... k*i,這樣時(shí)間復(fù)雜度為nlogn,因?yàn)槿绻貜?fù)次數(shù)大于一次,那么必定會(huì)有相鄰的兩個(gè)位置 i*k 和 i*k+i 的最長(zhǎng)公共前綴大于 i ,此時(shí)可以計(jì)算出重復(fù)次數(shù)ans為rmq(i*k,i*k+i)/i+1,這里的加一是因?yàn)楸旧淼囊淮?#xff0c;在計(jì)算前綴時(shí)會(huì)覆蓋掉,這里又出現(xiàn)了一個(gè)新的問(wèn)題,如果子串的起點(diǎn)相對(duì)于之前枚舉的位置在左右有所平移怎么辦,因?yàn)橄蛴矣?jì)算的已經(jīng)全部計(jì)算完畢了,我們只需要考慮向左是否還能擴(kuò)大答案,因?yàn)樵诿杜e的長(zhǎng)度 i 固定的情況下,此時(shí)最多向左平移?i 個(gè)單位了,因?yàn)槿绻^(guò)了 i 的話(huà),那么就完全可以歸為i*(k-1)與i*k的計(jì)算范圍內(nèi)了,所以為了湊整,我們令起點(diǎn)向左平移i-ans%i,此時(shí)的起點(diǎn)為i*k-(i-ans%i),我們記為st,再次計(jì)算看rmq(st,st+i)是否大于等于ans%i即可,為了方便起見(jiàn),因?yàn)楹竺娴淖址呀?jīng)連接起來(lái)了,所以我們將判斷條件更改為大于等于i就可以了,以此維護(hù)出現(xiàn)次數(shù)的最大值
以上為計(jì)算重復(fù)次數(shù)最多的連續(xù)重復(fù)子串的模板方法,下面說(shuō)一下如何求字典序最小
此時(shí)我們已經(jīng)獲得了出現(xiàn)次數(shù)為mmax的所有長(zhǎng)度的子字符串了,因?yàn)閟a數(shù)組是通過(guò)排名排序的,我們枚舉所有的sa數(shù)組,找到第一個(gè)滿(mǎn)足rmq(sa[i],sa[i]+length[j])>=(mmax-1)*length的下標(biāo)就是答案了,這里的mmax減一的原因和上面一樣,也是因?yàn)樵谟?jì)算前綴的時(shí)候會(huì)少算字符串本身
代碼:
#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數(shù)組,表示將S的n個(gè)后綴從小到大排序后把排好序的 //的后綴的開(kāi)頭位置順次放入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后的總長(zhǎng) {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后的總長(zhǎng) {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數(shù)組 {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; }?
總結(jié)
以上是生活随笔為你收集整理的POJ - 3693 Maximum repetition substring(后缀数组+RMQ)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CodeForces - 1096D E
- 下一篇: SPOJ - DISUBSTR Dist