【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 ...
随机推荐
- Linux学习笔记(六)压缩和解压缩命令
压缩和解压缩命令 zip unzip gzip gunzip bzip2 bunzip2 tar zip (.zip格式的压缩文件) 英文原意:package and compress (archiv ...
- 浅析Java7中的ConcurrentHashMap
引入ConcurrentHashMap 模拟使用hashmap在多线程场景下发生线程不安全现象 import java.util.HashMap; import java.util.Map; impo ...
- MySQL笔记总结-TCL语言
TCL语言 事务 一.含义 事务控制语言 Transaction Control Language 事务:一条或多条sql语句组成一个执行单位,一组sql语句要么都执行要么都不执行 二.特点(ACID ...
- 白话理解https
为什么需要加密? 因为http的内容是明文传输的,传输过程有可能被劫持或被篡改(中间人攻击),如何解决? 当然是加密.最简单的方式就是对称加密(快). 对称机密 就是一个密钥,可以理解为一把钥匙,我们 ...
- testlink配置修改
1. Read/write permissions报错 问题: Checking if /var/testlink/logs/ directory exists [S] </B< ...
- Flutter Weekly Issue 53
插件 left-scroll-actions A useful left scroll actions widget like WeChat.一款仿微信效果的 Flutter 左滑菜单插件.现在支持i ...
- C51_PID 水温控制系统
C51_PID 水温控制系统 51CPIDUART水温控制 前言 通过C语言程序写入51单片机实现水的温度的采集,并通过控制器控制加热器给水体加热,对水体的温进行PID控制,保证温度在设定值范围内波动 ...
- c语言实现乘法口诀表
利用c语言实现乘法口诀表的两种输出: #include <stdio.h> int main(int argc, char *argv[]) { ; ;a<;a++){//输出1-9 ...
- css之颜色表示法
css之颜色表示法 十六进制颜色 所有浏览器都支持十六进制颜色值. 十六进制颜色是这样规定的:#RRGGBB,其中的 RR(红色).GG(绿色).BB(蓝色)十六进制整数规定了颜色的成分.所有值必须介 ...
- 2019-05-22 BTC走势分析
转载于:https://juejin.im/post/5ce4adf5f265da1b8c195ff8