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 后缀数组 连续重复子串的更多相关文章

  1. POJ 3693 Maximum repetition substring ——后缀数组

    重复次数最多的字串,我们可以枚举循环节的长度. 然后正反两次LCP,然后发现如果长度%L有剩余的情况时,答案是在一个区间内的. 所以需要找到区间内最小的rk值. 两个后缀数组,四个ST表,$\Thet ...

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

    其实是论文题.. 题意:求一个字符串中,能由单位串repeat得到的子串中,单位串重复次数最多的子串.若有多个重复次数相同的,输出字典序最小的那个. 解题思路:其实跟论文差不多,我看了很久没看懂,后来 ...

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

    题意:给定一个字符串,求其中一个由循环子串构成且循环次数最多的一个子串,有多个就输出最小字典序的. 析:枚举循环串的长度ll,然后如果它出现了两次,那么它一定会覆盖s[0],s[ll],s[ll*2] ...

  4. POJ3693 Maximum repetition substring 后缀数组

    POJ - 3693 Maximum repetition substring 题意 输入一个串,求重复次数最多的连续重复字串,如果有次数相同的,则输出字典序最小的 Sample input ccab ...

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

    Description The repetition number of a string is defined as the maximum number R such that the strin ...

  6. 后缀数组 POJ 3693 Maximum repetition substring

    题目链接 题意:给定一个字符串,求重复次数最多的连续重复子串. 分析:(论文上的分析)先穷举长度 L,然后求长度为 L 的子串最多能连续出现几次.首先连续出现 1 次是肯定可以的,所以这里只考虑至少 ...

  7. POJ - 3693 Maximum repetition substring(重复次数最多的连续重复子串)

    传送门:POJ - 3693   题意:给你一个字符串,求重复次数最多的连续重复子串,如果有一样的,取字典序小的字符串. 题解: 比较容易理解的部分就是枚举长度为L,然后看长度为L的字符串最多连续出现 ...

  8. POJ 3693 Maximum repetition substring(连续重复子串)

    http://poj.org/problem?id=3693 题意:给定一个字符串,求重复次数最多的连续重复子串. 思路: 这道题确实是搞了很久,首先枚举连续子串的长度L,那么子串肯定包含了r[k], ...

  9. POJ3693 Maximum repetition substring —— 后缀数组 重复次数最多的连续重复子串

    题目链接:https://vjudge.net/problem/POJ-3693 Maximum repetition substring Time Limit: 1000MS   Memory Li ...

随机推荐

  1. C - N皇后问题 DFS

    在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上. 你的任务是,对于给定的N,求出有多少种合法的放置方法. Inpu ...

  2. Cucumber(1) —— 环境配置

    目录 学习资料 cucumber简介 cucumber环境配置 学习资料 1.cucumber官方学习网站 cucumber简介 1.cucumber是一种支持BBD(behavior-driven ...

  3. [PHP][thinkphp5] 学习二:路由、配置调用、常量定义调用

    路由: 其实TP5就是一个集多家框架所长而成的,感觉失去了自己的特色!路由这块呢类似于laravel框架!废话不说直接上码! 路由配置,类似laravel,就在route.php文件里配置路由(文件所 ...

  4. Springboot:定时任务处理(十三)

    构建一个定时任务的service接口及实现(模拟) 接口:com\applesnt\springboot\service\TaskService.java package com.applesnt.s ...

  5. 树莓派4b 上手三板斧

    树莓派4b 上手三板斧 1.无屏幕和网线连接准备 windows / mac 电脑下载安装Notepad++ 新建文件并保存为ssh(该文件为空文件) 新建文件wpa_supplicant.conf ...

  6. Spring Cloud+nacos+Feign,实现注册中心及配置中心

    写在前面 注册中心.配置中心的概念就不在这里解释了.发现服务原来一直用的是Eureka,因为这家伙闭源了,不爽.然后就发现了nacos,阿里巴巴的,好东西,一个搞定注册中心和配置中心.官网:https ...

  7. pytorch seq2seq模型中加入teacher_forcing机制

    在循环内加的teacher forcing机制,这种为目标确定的时候,可以这样加. 目标不确定,需要在循环外加. decoder.py 中的修改 """ 实现解码器 &q ...

  8. C/C++,被誉为“最经典的编程语言”,不仅是因为编程入门需要学?

    计算机诞生初期,用机器语言或汇编语言编写程序; 第一种高级语言FORTRAN诞生于1954年; BASIC语言(1964)是由FORTRAN语言的简化而成的是为初学者设计的小型高级语言; C语言是19 ...

  9. 数据库 MySQL 练习

    一.sql语句基础 1.顯示德國 Germany 的人口 SELECT population FROM world  WHERE name = 'Germany' 2.查詢面積為 5,000,000 ...

  10. thinkphp5和nginx不得不说的故事

    由于之前学习用的都是apsche,所以对ngnix一窍不通,在这里写给正在学习的同行,希望可以帮助到你们: 如果你不会用apache部署tp5的可以查看我之前发布的文章,里面有提到 phpstudy ...