[poj 3693]后缀数组+出现次数最多的重复子串
题目链接:http://poj.org/problem?id=3693
枚举长度L,看长度为L的子串最多能重复出现几次,首先,能出现1次是肯定的,然后看是否能出现两次及以上。由抽屉原理,这个子串出现次数>=2,那么必定会覆盖s[0],s[L],s[2L],...中相邻的两个,枚举是哪两个。对于覆盖了这两个的重复子串,它重复的次数就是看这两个后缀向前向后各自最多能匹配到多远。假设向前向后共匹配了长度K,那么重复的次数就是K/L+1。
这里有3个问题.
第一个,为什么先前向后各自匹配就可以了?因为子串长度就是L,枚举的这两个位置的距离也是L,那么这两个位置必定得是相同的。

第二个问题,怎么看向前最多匹配多少?向后的话直接通过height数组即可实现,而向前呢?难道要倒着再做一次后缀数组?(当然也不是不可行)事实上不用这么麻烦,直接考虑我们要求的结果K/L+1。利用整除的特性其实很容易得到这个结果。比如我们向后最多匹配L1。现在我们想知道结果能不能比L1/L+1来的大,怎么办呢?考虑L-L1%L,这个数代表L1至少要加几,才会让结果有所增加。那么显然向前距离在这个数以内的我们都不用检验了,因为即使检验到能匹配也没啥用,对结果没啥影响。所以我们检验一下距离是这个数的两个后缀的匹配长度,如果能匹配就更新一下结果。那么距离在这个数以外的呢?其实也不用检验了,如果说距离在这个数以外的还能让结果增加,那必须得再+L,再+L的话其实不必检验了,因为如果能匹配,在之前的求解(上次的枚举)中已经检验过了。因此,只检验一个位置即可。
第三个问题,怎么得到字典序最小的那一组解。通过sa数组的顺序枚举。不过得稍微优化一下,不能所有长度都尝试,不然会T。
穷举长度 L 的时间是 n,每次计算的时间是 n/L。所以整个做法的时间复杂度是 O(n/1+n/2+n/3+……+n/n)=O(nlogn)。(假设查询最长公共前缀的复杂度是O(1),用rmq预处理可以做到)
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std; const int MAXN = ;
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int wa[MAXN*],wb[MAXN*],wv[MAXN*],wss[MAXN*];
int c0(int *r,int a,int b)
{
return r[a] == r[b] && r[a+] == r[b+] && r[a+] == r[b+];
}
int c12(int k,int *r,int a,int b)
{
if(k == )
return r[a] < r[b] || ( r[a] == r[b] && c12(,r,a+,b+) );
else return r[a] < r[b] || ( r[a] == r[b] && wv[a+] < wv[b+] );
}
void sort(int *r,int *a,int *b,int n,int m)
{
int i;
for(i = ; i < n; i++)wv[i] = r[a[i]];
for(i = ; i < m; i++)wss[i] = ;
for(i = ; i < n; i++)wss[wv[i]]++;
for(i = ; i < m; i++)wss[i] += wss[i-];
for(i = n-; i >= ; i--)
b[--wss[wv[i]]] = a[i];
}
void dc3(int *r,int *sa,int n,int m)
{
int i, j, *rn = r + n;
int *san = sa + n, ta = , tb = (n+)/, tbc = , p;
r[n] = r[n+] = ;
for(i = ; i < n; i++)if(i % != )wa[tbc++] = i;
sort(r + , wa, wb, tbc, m);
sort(r + , wb, wa, tbc, m);
sort(r, wa, wb, tbc, m);
for(p = , rn[F(wb[])] = , i = ; i < tbc; i++)
rn[F(wb[i])] = c0(r, wb[i-], wb[i]) ? p - : p++;
if(p < tbc)dc3(rn,san,tbc,p);
else for(i = ; i < tbc; i++)san[rn[i]] = i;
for(i = ; i < tbc; i++) if(san[i] < tb)wb[ta++] = san[i] * ;
if(n % == )wb[ta++] = n - ;
sort(r, wb, wa, ta, m);
for(i = ; i < tbc; i++)wv[wb[i] = G(san[i])] = i;
for(i = , j = , p = ; i < ta && j < tbc; p++)
sa[p] = c12(wb[j] % , r, wa[i], wb[j]) ? wa[i++] : wb[j++];
for(; i < ta; p++)sa[p] = wa[i++];
for(; j < tbc; p++)sa[p] = wb[j++];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m)
{
for(int i = n; i < n*; i++)
str[i] = ;
dc3(str, sa, n+, m);
int i,j,k = ;
for(i = ; i <= n; i++)rank[sa[i]] = i;
for(i = ; i < n; i++)
{
if(k) k--;
j = sa[rank[i]-];
while(str[i+k] == str[j+k]) k++;
height[rank[i]] = k;
}
} int str[MAXN*3],sa[MAXN*3],rk[MAXN],height[MAXN];
int RMQ[MAXN];
int mm[MAXN];
int best[][MAXN];
void initRMQ(int n)
{
mm[]=-;
for(int i=; i<=n; i++)
mm[i]=((i&(i-))==)?mm[i-]+:mm[i-];
for(int i=; i<=n; i++)best[][i]=i;
for(int i=; i<=mm[n]; i++)
for(int j=; j+(<<i)-<=n; j++)
{
int a=best[i-][j];
int b=best[i-][j+(<<(i-))];
if(RMQ[a]<RMQ[b])best[i][j]=a;
else best[i][j]=b;
}
}
int askRMQ(int a,int b)
{
int t;
t=mm[b-a+];
b-=(<<t)-;
a=best[t][a];
b=best[t][b];
return RMQ[a]<RMQ[b]?a:b;
}
int lcp(int a,int b)
{
a=rk[a];
b=rk[b];
if(a>b)swap(a,b);
return height[askRMQ(a+,b)];
} char s[MAXN];
int cou;
int l;
int a[MAXN];
int cnt=; int main()
{
int cas=;
while (~scanf("%s",s) && s[]!='#')
{
l=strlen(s);
for (int i=; i<l; i++) str[i]=s[i]-'a'+;
str[l]=;
da(str,sa,rk,height,l,);
// for (int i=0;i<l;i++) printf("%d ",rk[i]);
for (int i=;i<=l;i++) RMQ[i]=height[i];
initRMQ(l);
cou=;
cnt=;
a[cnt++]=;
for (int L=; L<=l/; L++)
{
for (int j=; j+L<l; j+=L)
{
int back=lcp(j,j+L);
// printf(":%d:\n",back);
if (back/L+>cou)
{
cou=back/L+;
cnt=;
a[cnt++]=L;
}
else if (back/L+==cou) a[cnt++]=L;
int check=L-back%L;
if (check!=L && j-check>=)
{
int pre=lcp(j-check,j-check+L);
// printf(":%d:\n",pre);
if (pre/L+>cou)
{
cou=pre/L+;
cnt=;
a[cnt++]=L;
}
else if (pre/L+==cou) a[cnt++]=L;
}
}
}
// printf("::%d\n",cou);
int ansp=-;
int ansl=-;
// for (int i=1;i<=l;i++) printf("%d ",sa[i]);
for (int i=; i<=l&&ansp==-; i++)
{
int j=sa[i];
for (int k=; k<cnt&&a[k]<=(l-j)/cou; k++)
{
if (j+a[k]<l && lcp(j,j+a[k])/a[k]+>=cou)
{
// printf("QAQ\n");
ansp=j;
ansl=a[k];
break;
}
}
}
s[ansp+ansl*cou]=;
// printf("%d %d\n",ansp,ansl);
printf("Case %d: %s\n",++cas,s+ansp);
}
return ;
}
[poj 3693]后缀数组+出现次数最多的重复子串的更多相关文章
- POJ 1743 (后缀数组+不重叠最长重复子串)
题目链接: http://poj.org/problem?id=1743 题目大意:楼教主の男人八题orz.一篇钢琴谱,每个旋律的值都在1~88以内.琴谱的某段会变调,也就是说某段的数可以加减一个旋律 ...
- POJ 1743 后缀数组不重叠最长重复子串
#include<stdio.h> #include<string.h> #include<algorithm> #define maxn 30000 using ...
- poj 3693 后缀数组 重复次数最多的连续重复子串
Maximum repetition substring Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8669 Acc ...
- POJ - 2406 ~SPOJ - REPEATS~POJ - 3693 后缀数组求解重复字串问题
POJ - 2406 题意: 给出一个字符串,要把它写成(x)n的形式,问n的最大值. 这题是求整个串的重复次数,不是重复最多次数的字串 这题很容易想到用KMP求最小循环节就没了,但是后缀数组也能写 ...
- POJ 3693 后缀数组
题目链接:http://poj.org/problem?id=3693 题意:首先定义了一个字符串的重复度.即一个字符串由一个子串重复k次构成.那么最大的k即是该字符串的重复度.现在给定一个长度为n的 ...
- POJ 3693 (后缀数组) Maximum repetition substring
找重复次数最多的字串,如果有多解,要求字典序最小. 我也是跟着罗穗骞菊苣的论文才刷这道题的. 首先还是枚举一个循环节的长度L,如果它出现两次的话,一定会包含s[0], s[L], s[2L]这些相邻两 ...
- POJ 3693 后缀数组+RMQ
思路: 论文题 后缀数组&RMQ 有一些题解写得很繁 //By SiriusRen #include <cmath> #include <cstdio> #includ ...
- [Poj1743] [后缀数组论文例题] Musical Theme [后缀数组不可重叠最长重复子串]
利用后缀数组,先对读入整数处理str[i]=str[i+1]-str[i]+90这样可以避免负数,计算Height数组,二分答案,如果某处H<lim则将H数组分开,最终分成若干块,判断每块中是否 ...
- 【poj 2406】Power Strings 后缀数组DC3模板 【连续重复子串】
Power Strings 题意 给出一个字符串s,求s最多由几个相同的字符串重复而成(最小循环节的重复次数) 思路 之前学习KMP的时候做过. 我的思路是:枚举字符串的长度,对于当前长度k,判断\( ...
随机推荐
- Python学习 :六个标准数据类型
一.Numbers(数字类型) 数字类型主要分为两种—— 整数(Integer)与 浮点数(Float) 整数分为整型和长整型(在Python3中已经不再区分为整型与长整型,统一称为整型) 注意:数字 ...
- http一些常见知识记录
HTTP请求包(浏览器信息) 我们先来看看Request包的结构, Request包分为3部分,第一部分叫Request line(请求行), 第二部分叫Request header(请求头),第三部 ...
- C语言实例解析精粹学习笔记——34(用“结构”统计学生成绩)
实例34: 设学生信息包括学号.姓名和五门功课的成绩,要求编写输入输出学生信息的函数.在输入学生信息后,以学生成绩的总分从高到低顺序输出学生信息. 思路: 程序引入一个结构数组依次存储输入的学生信息, ...
- ESP32 学习笔记 - 环境搭建
打开终端 输入命令 sudo apt-get install gcc git wget make libncurses-dev flex bison gperf python python-seria ...
- JAVA中堆栈和内存分配详解(摘抄)
在Java中,有六个不同的地方可以存储数据: 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存 ...
- Dubbo原理及配置
技术交流群:233513714 Dubbo的背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进 ...
- ip4addr_ntoa和不可重入函数
在网络中,有一个转换IP地址到ASIIC字符串的函数,该函数的返回值所指向的ASIIC字符串驻留在静态内存中,所以该函数不可重入. 通俗的讲,在多任务系统中,一个任务执行在调用运行这个函数的时候,其他 ...
- preparedstatement execute()操作成功!但是返回false
转自http://blog.sina.com.cn/s/blog_963fb3af01013rcs.html Connection con = getConn(); String sql2 = &qu ...
- 软件工程项目组Z.XML会议记录 2013/09/18
软件工程项目组Z.XML会议记录 [例会时间]2013年9月18日周三21:00-23:00 [例会形式]小组讨论 [例会地点]三号公寓楼会客厅 [例会主持]李孟 [会议记录]毛宇 会议整体流程 一. ...
- android-ViewList的通用ViewHold
在写ViewList的时候要写Adapter的时候,经常大量的代码都是差不多的. 1 ViewHold 2 if(convertView ==null ){}else{} 3 setTag 4 FIn ...