POJ-3693 Maximum repetition substring 后缀数组
2013-04-24 17:22
567 查看
题目链接:http://poj.org/problem?id=3693
求字符串的重复次数最多的且字典序最小的字串。
很不错的题目。罗穗骞大牛论文的模板题,摘了Neo / Add ~0U>>1大牛的详细题解,如下:
首先求第一问最大重复数。从N的范围来看O(N^2)虽不靠谱,但是起码能带来些有用的启示。方法有二,一是枚举开头位置求重复长度;二是枚举重复长度求开头位置。
第一种方法求最大重复数的方法MS也只有枚举重复长度然后去判……所以说我们从第二个方法入手。O(N^2)的方法是再枚举开头位置。我们来考虑一下能不能少枚举一些开头位置——更确切地说,能不能只枚举一些特殊位置,设当前枚举的长度为L,能不能只枚举0,L,2L,……这样的位置。见下图:
首先明确,枚举长度Len的时候,从一个位置求出的Lcp值表明从此处开始重复数为floor(Lcp / Len) + 1。
然后容易发现的问题就是,可能从某个枚举位置向前移动“一点”能够多一个周期,从而使重复数增加。从图上来看,这种状况发生的条件就是Lcp比满足这个重复数“必需的Lcp”要多,也就是Lcp % Len ≠ 0。不难发现,这时候我们向前移动Len - Lcp % Len的话,如果有下一个周期就会进去,否则也不会引起新的变化。所以说这种方法可行。
这样的话复杂度为n(1 + 1/2 + 1/3 + ... + 1/n) = O(nlogn)。有一个显而易见的优化,那就是只需要枚举到floor(n / 2)即可。但是后面的部分也少不了多少……
第二问是求最小字典序的答案。我采用了这样一种方法,那就是在第一步中记录能够达到最大重复数的重复长度的集合,然后按照sa[1],sa[2],sa[3],……的顺序去暴力枚举,每到一个位置就从小到大地判重复长度的集合中有没有可行的,如果有一个可行的就立即输出结果并退出。这样的做法能够保证正确性,虽然最坏情况下仍然有可能退化到O(n^2),但是要构造这样一组数据是比较困难的(得一直枚举到最后一个后缀,还得从1到Len / 2都能满足最大重复数)。对于非特殊构造的数据这一步的复杂度几乎是O(n),于是这个题就可以水过了。
我的代码:
求字符串的重复次数最多的且字典序最小的字串。
很不错的题目。罗穗骞大牛论文的模板题,摘了Neo / Add ~0U>>1大牛的详细题解,如下:
首先求第一问最大重复数。从N的范围来看O(N^2)虽不靠谱,但是起码能带来些有用的启示。方法有二,一是枚举开头位置求重复长度;二是枚举重复长度求开头位置。
第一种方法求最大重复数的方法MS也只有枚举重复长度然后去判……所以说我们从第二个方法入手。O(N^2)的方法是再枚举开头位置。我们来考虑一下能不能少枚举一些开头位置——更确切地说,能不能只枚举一些特殊位置,设当前枚举的长度为L,能不能只枚举0,L,2L,……这样的位置。见下图:
首先明确,枚举长度Len的时候,从一个位置求出的Lcp值表明从此处开始重复数为floor(Lcp / Len) + 1。
然后容易发现的问题就是,可能从某个枚举位置向前移动“一点”能够多一个周期,从而使重复数增加。从图上来看,这种状况发生的条件就是Lcp比满足这个重复数“必需的Lcp”要多,也就是Lcp % Len ≠ 0。不难发现,这时候我们向前移动Len - Lcp % Len的话,如果有下一个周期就会进去,否则也不会引起新的变化。所以说这种方法可行。
这样的话复杂度为n(1 + 1/2 + 1/3 + ... + 1/n) = O(nlogn)。有一个显而易见的优化,那就是只需要枚举到floor(n / 2)即可。但是后面的部分也少不了多少……
第二问是求最小字典序的答案。我采用了这样一种方法,那就是在第一步中记录能够达到最大重复数的重复长度的集合,然后按照sa[1],sa[2],sa[3],……的顺序去暴力枚举,每到一个位置就从小到大地判重复长度的集合中有没有可行的,如果有一个可行的就立即输出结果并退出。这样的做法能够保证正确性,虽然最坏情况下仍然有可能退化到O(n^2),但是要构造这样一组数据是比较困难的(得一直枚举到最后一个后缀,还得从1到Len / 2都能满足最大重复数)。对于非特殊构造的数据这一步的复杂度几乎是O(n),于是这个题就可以水过了。
我的代码:
//STATUS:C++_AC_422MS_11232KB #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<iostream> #include<string> #include<algorithm> #include<vector> #include<queue> #include<stack> #include<map> using namespace std; #define LL __int64 #define pii pair<int,int> #define mem(a,b) memset(a,b,sizeof(a)) #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 #define PI acos(-1.0) const int N=100010,INF=0x3f3f3f3f,MOD=10000,STA=8000010; //const LL LNF=0x3f3f3f3f3f3f3f3f; const double DNF=1e13; // inline int Max(int a,int b){return a>b?a:b;} inline int Min(int a,int b){return a<b?a:b;} void swap(int& a,int& b){int t=a;a=b;b=t;} void swap(LL& a,LL& b){LL t=a;a=b;b=t;} // char s ; int d [20]; int num ; int sa ,t1 ,t2 ,c ,rank ,height ; int n,m; void build_sa(int s[],int n,int m) { int i,k,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(k=1;k<=n;k<<=1){ p=0; //直接利用sa数组排序第二关键字 for(i=n-k;i<n;i++)y[p++]=i; for(i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; //基数排序第一关键字 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]; //根据sa和x数组计算新的x数组 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]+k]==y[sa[i]+k]?p-1:p++; if(p>=n)break; //已经排好序,直接退出 m=p; //下次基数排序的最大值 } } void getHeight(int s[],int n) { int i,j,k=0; for(i=0;i<=n;i++)rank[sa[i]]=i; for(i=0;i<n;i++){ if(k)k--; j=sa[rank[i]-1]; while(s[i+k]==s[j+k])k++; height[rank[i]]=k; } } void rmq_init(int a[]) { int i,j; for(i=1;i<=n;i++)d[i][0]=a[i]; for(j=1;(1<<j)<=n;j++){ for(i=1;i+(1<<j)-1<=n;i++){ d[i][j]=Min(d[i][j-1],d[i+(1<<(j-1))][j-1]); } } } int rmq(int l,int r) { int k=0; while((1<<(k+1))<=r-l+1)k++; return Min(d[l][k],d[r-(1<<k)+1][k]); } int lcp(int a,int b) { if(a==b)return n-a; int ra=rank[a],rb=rank[b]; if(ra>rb)swap(ra,rb); ra++; return rmq(ra,rb); } int main() { // freopen("in.txt","r",stdin); int i,j,sz=1,hig,ans ,cnt,fre,wide,t,ss,ok; while(~scanf("%s",s) && (s[0]!='#' || s[1]) ) { n=strlen(s); for(i=0;i<n;i++){ num[i]=s[i]-'a'+1; } num =0; m=27; build_sa(num,n+1,m); getHeight(num,n); rmq_init(height); hig=0; for(i=1;i<=n/2;i++){ for(j=0;j+i<n;j+=i){ wide=lcp(j,j+i); fre=wide/i+1; t=j-(i-wide%i); if(t>=0 && wide%i){ wide=lcp(t,t+i); fre=Max(fre,wide/i+1); } if(fre>hig){ hig=fre; cnt=0; ans[cnt++]=i; } else if(fre==hig){ ans[cnt++]=i; } } } ok=0; for(i=1;i<=n;i++){ for(j=0;j<cnt;j++){ if(sa[i]+ans[j]>=n)continue; wide=lcp(sa[i],sa[i]+ans[j]); if(wide/ans[j]+1==hig){ ss=sa[i]; s[ss+hig*ans[j]]=0; ok=1; break; } } if(ok)break; } printf("Case %d: %s\n",sz++,s+ss); } return 0; }
相关文章推荐
- POJ 3693 Maximum repetition substring(后缀数组[重复次数最多的连续重复子串])
- POJ 3693 Maximum repetition substring(后缀数组求最长重复子串)
- POJ - 3693 Maximum repetition substring(后缀数组求重复次数最多的连续重复子串)
- POJ 3693 Maximum Repetition Substring <后缀数组 + RMQ>
- POJ-3693-Maximum repetition substring(后缀数组-重复次数最多的连续重复子串)
- POJ - 3693 Maximum repetition substring 后缀数组 分块
- 【POJ】3693 Maximum repetition substring 【后缀数组——求最长连续重复字串】
- 【后缀数组】【poj 3693】Maximum repetition substring
- POJ 3693 Maximum repetition substring 后缀数组 暴力 rmq
- 【后缀数组】 HDOJ 2459 && POJ 3693 Maximum repetition substring
- POJ 3693 Maximum repetition substring(后缀数组神题)
- POJ 3693 Maximum repetitionsubstring(后缀数组:循环子串)
- POJ 3693 Maximum Repetition Substring 后缀数组
- POJ 3693 Maximum repetition substring 后缀数组与区间最值的完美结合
- POJ 3693 Maximum repetition substring 后缀数组求重复次数最多子串
- POJ 3693 Maximum repetition substring 后缀数组 + RMQ预处理
- Poj 3693 Maximum repetition substring|后缀数组|st表
- HDU 2459 PKU 3693 Maximum repetition substring 后缀数组 RMQ
- POJ 题目 3693 Maximum repetition substring(后缀数组+RMQ+枚举求最小字典序的重复次数最多的子串)
- POJ3693 Maximum repetition substring