题目链接: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),于是这个题就可以水过了。

  我的代码:

  1 //STATUS:C++_AC_422MS_11232KB
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<math.h>
  6 #include<iostream>
  7 #include<string>
  8 #include<algorithm>
  9 #include<vector>
 10 #include<queue>
 11 #include<stack>
 12 #include<map>
 13 using namespace std;
 14 #define LL __int64
 15 #define pii pair<int,int>
 16 #define mem(a,b) memset(a,b,sizeof(a))
 17 #define lson l,mid,rt<<1
 18 #define rson mid+1,r,rt<<1|1
 19 #define PI acos(-1.0)
 20 const int N=100010,INF=0x3f3f3f3f,MOD=10000,STA=8000010;
 21 //const LL LNF=0x3f3f3f3f3f3f3f3f;
 22 const double DNF=1e13;
 23 //
 24 inline int Max(int a,int b){return a>b?a:b;}
 25 inline int Min(int a,int b){return a<b?a:b;}
 26 void swap(int& a,int& b){int t=a;a=b;b=t;}
 27 void swap(LL& a,LL& b){LL t=a;a=b;b=t;}
 28 //
 29
 30 char s[N];
 31 int d[N][20];
 32 int num[N];
 33 int sa[N],t1[N],t2[N],c[N],rank[N],height[N];
 34 int n,m;
 35
 36 void build_sa(int s[],int n,int m)
 37 {
 38     int i,k,p,*x=t1,*y=t2;
 39     //第一轮基数排序
 40     for(i=0;i<m;i++)c[i]=0;
 41     for(i=0;i<n;i++)c[x[i]=s[i]]++;
 42     for(i=1;i<m;i++)c[i]+=c[i-1];
 43     for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
 44     for(k=1;k<=n;k<<=1){
 45         p=0;
 46         //直接利用sa数组排序第二关键字
 47         for(i=n-k;i<n;i++)y[p++]=i;
 48         for(i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
 49         //基数排序第一关键字
 50         for(i=0;i<m;i++)c[i]=0;
 51         for(i=0;i<n;i++)c[x[y[i]]]++;
 52         for(i=1;i<m;i++)c[i]+=c[i-1];
 53         for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
 54         //根据sa和x数组计算新的x数组
 55         swap(x,y);
 56         p=1;x[sa[0]]=0;
 57         for(i=1;i<n;i++)
 58             x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
 59         if(p>=n)break;   //已经排好序,直接退出
 60         m=p;     //下次基数排序的最大值
 61     }
 62 }
 63
 64 void getHeight(int s[],int n)
 65 {
 66     int i,j,k=0;
 67     for(i=0;i<=n;i++)rank[sa[i]]=i;
 68     for(i=0;i<n;i++){
 69         if(k)k--;
 70         j=sa[rank[i]-1];
 71         while(s[i+k]==s[j+k])k++;
 72         height[rank[i]]=k;
 73     }
 74 }
 75
 76 void rmq_init(int a[])
 77 {
 78     int i,j;
 79     for(i=1;i<=n;i++)d[i][0]=a[i];
 80     for(j=1;(1<<j)<=n;j++){
 81         for(i=1;i+(1<<j)-1<=n;i++){
 82             d[i][j]=Min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
 83         }
 84     }
 85 }
 86
 87 int rmq(int l,int r)
 88 {
 89     int k=0;
 90     while((1<<(k+1))<=r-l+1)k++;
 91     return Min(d[l][k],d[r-(1<<k)+1][k]);
 92 }
 93
 94 int lcp(int a,int b)
 95 {
 96     if(a==b)return n-a;
 97     int ra=rank[a],rb=rank[b];
 98     if(ra>rb)swap(ra,rb);
 99     ra++;
100     return rmq(ra,rb);
101 }
102
103 int main()
104 {
105  //   freopen("in.txt","r",stdin);
106     int i,j,sz=1,hig,ans[N],cnt,fre,wide,t,ss,ok;
107     while(~scanf("%s",s) && (s[0]!='#' || s[1]) )
108     {
109         n=strlen(s);
110         for(i=0;i<n;i++){
111             num[i]=s[i]-'a'+1;
112         }
113         num[n]=0;
114         m=27;
115         build_sa(num,n+1,m);
116         getHeight(num,n);
117         rmq_init(height);
118
119         hig=0;
120         for(i=1;i<=n/2;i++){
121             for(j=0;j+i<n;j+=i){
122                 wide=lcp(j,j+i);
123                 fre=wide/i+1;
124                 t=j-(i-wide%i);
125                 if(t>=0 && wide%i){
126                     wide=lcp(t,t+i);
127                     fre=Max(fre,wide/i+1);
128                 }
129                 if(fre>hig){
130                     hig=fre;
131                     cnt=0;
132                     ans[cnt++]=i;
133                 }
134                 else if(fre==hig){
135                     ans[cnt++]=i;
136                 }
137             }
138         }
139
140         ok=0;
141         for(i=1;i<=n;i++){
142             for(j=0;j<cnt;j++){
143                 if(sa[i]+ans[j]>=n)continue;
144                 wide=lcp(sa[i],sa[i]+ans[j]);
145                 if(wide/ans[j]+1==hig){
146                     ss=sa[i];
147                     s[ss+hig*ans[j]]=0;
148                     ok=1;
149                     break;
150                 }
151             }
152             if(ok)break;
153         }
154
155         printf("Case %d: %s\n",sz++,s+ss);
156     }
157     return 0;
158 }

转载于:https://www.cnblogs.com/zhsl/archive/2013/04/24/3040629.html

POJ-3693 Maximum repetition substring 后缀数组相关推荐

  1. POJ - 3693 Maximum repetition substring(后缀数组+RMQ)

    题目链接:点击查看 题目大意:给出一个字符串,求出字符串中 重复次数最多的连续重复子串 ,如果有多个答案,输出字典序最小的 题目分析:又是一个模板题,这里放一个大佬的博客,讲的很清楚: https:/ ...

  2. POJ 3693 Maximum repetition substring (后缀数组)

    题目大意: 求出字典序最小,重复次数最多,的子串. 思路分析: RMQ + height 数组可以求出任意两个后缀的lcp 我们枚举答案字符串的重复的长度. 如果这个字符串的长度为 l ,而且这个字符 ...

  3. 牛客-139 I. Substring(后缀数组 or 后缀自动机)

    牛客-139 I. Substring(后缀数组 or 后缀自动机) 题目链接 题意 一个由{a,b,c}\{a, b, c\}{a,b,c}组成的字符串SSS,求S子串的最大的集合,使得集合里的字符 ...

  4. POJ3693 Maximum repetition substring

    题目 The repetition number of a string is defined as the maximum number R such that the string can be ...

  5. HDU 5769 Substring(后缀数组)

    Substring Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total ...

  6. HDU - 5769 Substring(后缀数组)

    题目链接:点击查看 题目大意:给出一个字符串 s 和一个字符 ch,问字符串 s 中有多少个本质不同的子串包含字符 ch 题目分析:其实就是求本质不同的子串加了一点条件,对于每个sa[ i ],其本质 ...

  7. POJ - 3261 Milk Patterns(二分+后缀数组)

    题目链接:点击查看 题目大意:给出一个字符串,以及一个k,现在求出现次数大于等于k次的最大可重叠子串的长度 题目分析:可以说是后缀数组的模板题目了吧..直接跑出height数组,因为height数组代 ...

  8. POJ - 3450 Corporate Identity(二分+后缀数组)

    题目链接:点击查看 题目大意:给出n个字符串,求出n个字符串中最长的公共子串,如果没有,输出IDENTITY LOST 题目分析:可以直接用二分+后缀数组来做,先将n个字符串连接起来,中间用一个不同的 ...

  9. POJ - 2774 Long Long Message(后缀数组)

    题目链接:点击查看 题目大意:给出两个字符串,求出其最长的公共子串 题目分析:后缀数组水题,直接用一个不同的符号拼接一下两个字符串,然后跑出height数组和sa数组,再遍历一遍取最大值就是答案了,需 ...

最新文章

  1. 滤波器的主要特性指标
  2. C++跨类调用——extern
  3. Vs2012 打开项目 自动关闭 并停止工作 解决方法
  4. ubuntu14.04 upgrade出现【Ubuntu is running in low-graphics mode】问题的一个解决办法
  5. python中的画布背景设置_教你用python画图—Turtle详细教程
  6. 使用jQuery的ajax同步请求吃过的亏
  7. (新聞) 友達光電 A+種子暑期實習計畫 埋下希望的光電種子 (日記)
  8. PHP将mysql数据导出为Excel
  9. Code[VS]1273 风战
  10. python编程输入法_用Python写一个拼音输入法
  11. 电子发票对报销类saas的影响
  12. 游戏美术基础:游戏贴图
  13. 【饭谈】面试官:速斩此子,切不可引狼入室
  14. 九连环问题(Java)
  15. NPDP知识推送-第一章新产品开发战略(1)
  16. 这些大厂笔试题 你都见识(被无情鞭挞)过了吗?—— 哔哩哔哩篇
  17. 漏洞深度分析|Thinkphp 多语言 RCE
  18. osgearth仿真平台之特效(4)
  19. JavaScript 中的Element对象
  20. 【EmTech China 2018】不容错过的科技盛典!(附萌新临时充电手册)

热门文章

  1. 2018-2019-1 20165303 实验五 通讯协议设计
  2. vscode设置中文,设置中文不成功问题
  3. Confluence 6 在 Apache 或者系统级别阻止垃圾
  4. 使用轻量级Spring @Scheduled注解执行定时任务
  5. shape的简单用法
  6. linux 新建用户、用户组 以及为新用户分配权限
  7. 蛋疼的中文编码及其计算机编码历史
  8. matlab可以使用词云分析吗,利用豆瓣短评数据生成词云
  9. 微信小程序里如何使用npm?小程序集成友盟举例
  10. Android 自定义年月日日期选择器、时分时间选择器