【Poj-3693】Maximum repetition substring 后缀数组 连续重复子串
POJ - 3693
题意
SPOJ - REPEATS的进阶版,在这题的基础上输出字典序最小的重复字串。
思路
跟上题一样,先求出最长的重复次数,在求的过程中顺便纪录最多次数可能的长度。
因为sa数组是按照字典序排好的,所以我们顺序遍历sa数组,找到第一个符合的输出即可。
why 字符串结尾加0
我懵了,看不懂论文中的解释(下图)

论文中的解释是说 这样搞,在cmp函数中就不用加越界判断。(我之前也好奇为啥cmp中不用加越界。。。)
下面解释是我自己的理解,不一定准确
原因:
如果不加一个前面没有出现过的字符,那么在求height的时候可能会出问题:
\(while(str[i+k]==str[sa[rk[i]-1]+k]) ++k;\)
上面求\(height\)的代码中并没有判断\(i+k\)以及\(sa[rk[i]-1]+k\)是否越界,
因此两个式子中的一个越界的时候,假如之前的样例存在比当前字符串长的,
并且越界之后\(str[i+k]\)还和\(str[sa[rk[i]-1]+k]\)相等,这样height数组就错了。
加前面没有出现过的字符,就是为了书写方便,越界之后循环就自己退出了。
为什么要加0呢?
有些代码字符串下标是从0开始,在求sa数组的时候,要加一个字符,
顺便把字符串的扩展到了下标n,这时如果加的不是0,而是一个>= 字符串中最小字符 的一个字符的话,
那么后缀n就会影响到sa数组的正确性。
而加0,正好使得\(rk[n]==0\),\(sa[0]=n\),后缀0~n-1的排名全在1-n之间。
综上:
字符串下标从1开始,加一个没有出现过的字符就可以。
下标从0开始,加一个<=出现过的最小字符就可以:0
代码
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<math.h>
#define pb push_back
typedef long long ll;
using namespace std;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 1e5+10;
int sa[N],cnt[N],pos[N],rk[N],oldrk[N],ht[N],n,m;
char str[N];
bool cmp(int a,int b,int k)
{
return oldrk[a]==oldrk[b]&&oldrk[a+k]==oldrk[b+k];
}
void getsa()
{
memset(cnt,0,sizeof(cnt));
m=122;
for(int i=1; i<=n; ++i) ++cnt[rk[i]=str[i]];
for(int i=1; i<=m; ++i) cnt[i]+=cnt[i-1];
for(int i=n; i; i--) sa[cnt[rk[i]]--]=i;
for(int k=1; k<=n; k<<=1)
{
int num=0;
for(int i=n-k+1; i<=n; ++i) pos[++num]=i;
for(int i=1; i<=n; ++i) if(sa[i]>k) pos[++num]=sa[i]-k;
memset(cnt,0,sizeof(cnt));
for(int i=1; i<=n; ++i) ++cnt[rk[i]];
for(int i=1; i<=m; ++i) cnt[i]+=cnt[i-1];
for(int i=n; i; i--) sa[cnt[rk[pos[i]]]--]=pos[i];
num=0;
memcpy(oldrk,rk,sizeof(rk));
for(int i=1; i<=n; ++i) rk[sa[i]]=cmp(sa[i],sa[i-1],k)?num:++num;
if(num==n) break;
m=num;
}
for(int i=1; i<=n; ++i)
rk[sa[i]]=i;
int k=0;
for(int i=1; i<=n; ++i)
{
if(k) --k;
while(str[i+k]==str[sa[rk[i]-1]+k]) ++k;
//下面就是加上越界判断
// while(i+k<=n&&sa[rk[i]-1]+k<=n&&str[i+k]==str[sa[rk[i]-1]+k])
// ++k;
ht[rk[i]]=k;
}
}
int dp[N][20];
void RMQ()
{
for(int i=1; i<=n; ++i) dp[i][0]=ht[i];
for(int j=1; (1<<j)<=n; ++j)
{
for(int i=1; i+(1<<j)-1<=n; ++i)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
int query(int l,int r)
{
int k=0;
while((1<<(k+1))<=(r-l+1)) ++k;
//int k=int(log(r-l+1.0)/log(2.0));// 比上面慢
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int lcp(int i,int j)
{
i=rk[i],j=rk[j];
if(i>j) swap(i,j);
return query(i+1,j);
}
int tot,len[N];
int main()
{
int cas=0;
while(~scanf("%s",str+1)&&strcmp(str+1,"#"))
{
tot=0;
n=strlen(str+1);
str[n+1]='c';
getsa();
RMQ();
printf("Case %d: ",++cas);
int ans=0;
for(int i=1; i<=n; ++i)
{
for(int j=1; j+i<=n; j+=i)
{
int now=lcp(j,j+i);
int num=now/i+1;
int k=j-(i-now%i);
if(k>0&&lcp(k,k+i)>=i) ++num;
if(num>ans)
{
ans=num;
tot=0;
len[tot++]=i;
}
else if(num==ans)
{
if(len[tot-1]!=i)
len[tot++]=i;
}
}
}
int flag=0;
for(int i=1; i<=n; ++i)
{
for(int j=0; j<tot; ++j)
{
int l=len[j];
if(lcp(sa[i],sa[i]+l)>=(ans-1)*l)
{
str[sa[i]+ans*l]='\0';//使用结束符比一个个输出快
printf("%s\n",str+sa[i]);
flag=1;
break;
}
}
if(flag)
break;
}
}
return 0;
}
/*
*/
【Poj-3693】Maximum repetition substring 后缀数组 连续重复子串的更多相关文章
- POJ 3693 Maximum repetition substring ——后缀数组
重复次数最多的字串,我们可以枚举循环节的长度. 然后正反两次LCP,然后发现如果长度%L有剩余的情况时,答案是在一个区间内的. 所以需要找到区间内最小的rk值. 两个后缀数组,四个ST表,$\Thet ...
- poj 3693 Maximum repetition substring (后缀数组)
其实是论文题.. 题意:求一个字符串中,能由单位串repeat得到的子串中,单位串重复次数最多的子串.若有多个重复次数相同的,输出字典序最小的那个. 解题思路:其实跟论文差不多,我看了很久没看懂,后来 ...
- POJ 3693 Maximum repetition substring (后缀数组+RMQ)
题意:给定一个字符串,求其中一个由循环子串构成且循环次数最多的一个子串,有多个就输出最小字典序的. 析:枚举循环串的长度ll,然后如果它出现了两次,那么它一定会覆盖s[0],s[ll],s[ll*2] ...
- POJ3693 Maximum repetition substring 后缀数组
POJ - 3693 Maximum repetition substring 题意 输入一个串,求重复次数最多的连续重复字串,如果有次数相同的,则输出字典序最小的 Sample input ccab ...
- POJ 3693 Maximum repetition substring(后缀数组)
Description The repetition number of a string is defined as the maximum number R such that the strin ...
- 后缀数组 POJ 3693 Maximum repetition substring
题目链接 题意:给定一个字符串,求重复次数最多的连续重复子串. 分析:(论文上的分析)先穷举长度 L,然后求长度为 L 的子串最多能连续出现几次.首先连续出现 1 次是肯定可以的,所以这里只考虑至少 ...
- POJ - 3693 Maximum repetition substring(重复次数最多的连续重复子串)
传送门:POJ - 3693 题意:给你一个字符串,求重复次数最多的连续重复子串,如果有一样的,取字典序小的字符串. 题解: 比较容易理解的部分就是枚举长度为L,然后看长度为L的字符串最多连续出现 ...
- POJ 3693 Maximum repetition substring(连续重复子串)
http://poj.org/problem?id=3693 题意:给定一个字符串,求重复次数最多的连续重复子串. 思路: 这道题确实是搞了很久,首先枚举连续子串的长度L,那么子串肯定包含了r[k], ...
- POJ3693 Maximum repetition substring —— 后缀数组 重复次数最多的连续重复子串
题目链接:https://vjudge.net/problem/POJ-3693 Maximum repetition substring Time Limit: 1000MS Memory Li ...
随机推荐
- JZ2440 linux-3.4.2内核启动报错:Verifying Checksum ... Bad Data CRC
使用的uboot版本是1.1.6,是打过u-boot-1.1.6_jz2440.patch的: kernel使用的版本是3.4.2, 也是打过linux-3.4.2_camera_jz2440.pat ...
- 一不小心实现了RPC
前言 随着最近关注 cim 项目的人越发增多,导致提的问题以及 Bug 也在增加,在修复问题的过程中难免代码洁癖又上来了. 看着一两年前写的东西总是怀疑这真的是出自自己手里嘛?有些地方实在忍不住了便开 ...
- centos7.4安装docker
安装docker的前提条件 1)关闭系统的防火墙和selinux 2) 同步系统时间 3)系统必须是centos7以上 移除旧版本yum remove docker docker-client do ...
- 使用Spring Boot搭建你的第一个应用程序
文章目录 依赖配置 main程序配置 MVC配置 安全配置 存储 Web 页面和Controller 异常处理 测试 结论 Spring Boot是Spring平台的约定式的应用框架,使用Spring ...
- 【linux题目】第三关
1. 解释下什么是GPL,GNU,自由软件? 2. 如何选择Linux操作系统版本? 3. 安装系统时如何给Linux操作系统分区? 4. 描述Linux系统的启动过程? 5. 简要说出20个Linu ...
- npm小工具、技巧合集:让你的npm“健步如飞”
1.解决安装速度慢问题-nrm 解决痛点 由于npm的包仓库是在国外,npm包安装速度较慢,部分包甚至无法安装. 对比cnpm的优势 1.cnpm增加了大脑的记忆和思维负担,常常需要考虑使用npm还是 ...
- mac 关闭系统完整性保护 SIP(System Integrity Protection)的方法
在 OS X El Capitan 中有一个跟安全相关的模式叫 SIP(System Integrity Protection ) ,它禁止让软件以 root 身份来在 mac 上运行,并且对于目录 ...
- 谈谈你对vuex的理解
vuex创建公有仓库的插件 1.储存公共状态 2.能够根据事件来修改状态 3.多个组件都需要变化,有机制把这个新的状态通知给所有的组件 vuex中的四个类 1.state 定义需要共享的状态 2 ...
- 关于:Express会被Koa2取代吗?
知会上看到有个问题<Express会被Koa2取代吗?>.刚好对Express.koa有点小研究,于是简单回答了一下. 1.先说结论 目前没有看到Express会被koa2取代的迹象. 目 ...
- 开始导入第一个第三方库jieba
在做python的练习题,想看看运行结果. 谁知,有道题,不能识别jieba,原来要导入,因为是第三方库,照着书里面的导入方法,有三种,一种是用pip,在命令行里面安装,使用pip - p 可以查看p ...