[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,判断\( ...
随机推荐
- SKIP(插入空行)
WRITE 'This is the 1st line'. SKIP. WRITE 'This is the 2nd line'. 跳转至某一行 SKIP TO LINE line_number. 插 ...
- Hadoop学习(一) Hadoop是什么
Hadoop是什么? Hadoop是一个开发和运行处理大规模数据的软件平台,是Appach的一个用Java语言实现开源软件框架,实现在大量计算机组成的集群中对海量数据进行分布式计算. Hadoop框架 ...
- C++11中decltype的使用
The decltype type specifier yields the type of a specified expression. The decltype type specifier, ...
- 创龙DSP6748开发板驱动LCD屏
1. DSP6748内部有2个LCD控制器,Raster Controller 光栅控制器和the LCD Interface Display Driver (LIDD) controller 控制器 ...
- PHP批量替换MySql数据库中的数据内容
<?php //替换数据库内容类 class replace{ public $dbAddress; //数据库地址 public $dbUser; //数据库用户名 public $dbPwd ...
- 纯js生成QRCode
纯js,不依赖jquery,非常好用,废话不多说,直接上代码! <!DOCTYPE html> <html> <head> <meta charset=&qu ...
- Hadoop入门案列,初学者Coder
1.WordCount Job类: package com.simope.mr.wcFor; import org.apache.hadoop.conf.Configuration; import o ...
- fiddler抓包-简单易操作(二)
Fiddler抓包简介 原理:fiddler是通过改写HTTP代理,客户端和服务器进行交互时,数据会从他那里通过,来监控和截取数据.我是这样理解的,如果不对,欢迎指正.如下图: 如果想要抓到数据包,首 ...
- Spark概念介绍
Spark概念介绍:spark应用程序在集群中以一系列独立的线程运行,通过驱动器程序(Driver Program)发起一系列的并行操作.SparkContext对象作为中间的连接对象,通过Spark ...
- CentOS环境安装JDK(二)
安装JDK-7u79-linux-x64 打开虚拟机,进入终端: 1.假设用户名是tianjiale(则需要进入管理员角色,既root) (1).将用户名tianjiale添加到sudoer列表中 提 ...