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. asp.net mvc 接收jquery ajax发送的数组对象

    <script type="text/javascript"> $(function () { var obj = { name: "军需品", m ...

  2. ES[7.6.x]学习笔记(三)新建索引

    与ES的交互方式 与es的交互方式采用http的请求方式,请求的格式如下: curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT& ...

  3. TensorFlow keras dropout层

    # 建立神经网络模型 model = keras.Sequential([ keras.layers.Flatten(input_shape=(28, 28)), # 将输入数据的形状进行修改成神经网 ...

  4. springmvc配置数据源方式

    1 阿里巴巴的druid数据源 <!-- 配置数据源 使用的是Druid数据源 -->-<bean destroy-method="close" init-met ...

  5. Java 自定义注解及注解读取解析--模拟框架生成SQL语句

    假设们使用一张简单的表,结构如下: 定义注解: 表注解: package com.xzlf.annotation; import java.lang.annotation.ElementType; i ...

  6. SVM之不一样的视角

    在上一篇学习SVM中 从最大间隔角度出发,详细学习了如何用拉格朗日乘数法求解约束问题,一步步构建SVM的目标函数,这次尝试从另一个角度学习SVM. 回顾监督学习要素 数据:(\(x_i,y_i\)) ...

  7. web前端该怎么入门?web前端入门教程(非常详细)

    初学编程的小伙伴经常会遇到的问题,1.没资源 2.没人带 3.不知道从何开始 ,小编也是从新手期过来的,所以很能理解萌新的难处,现在整理一些以前自己学习的一些资料送给大家,希望对广大初学小伙伴有帮助! ...

  8. Ubuntu创建WiFi:16.0.4

    点击编辑链接,点击桌面状态栏的网络图标 点击增加 类型选择WiFi 名称.SSID,均要填写,模式选择:热点 wifi安全性:选择一个安全模式,这里选的是, wpa 及 wpa2个人 必须说的是:选择 ...

  9. CG-CTF(2)

    CG-CTF https://cgctf.nuptsast.com/challenges#Web 续上~ 第七题:单身二十年 查看源代码: 取得flag(干杯~): 本题也可通过burp抓包,查看返回 ...

  10. Python带你做个愉快的"动森"玩家! (超简单代码)

    最近Switch上的<动物森友会>可谓是炙手可热,它几乎算是任天堂版的<模拟人生>了,它的最新游戏<集合啦!动物森友会>(以下称“动森”)在发售后,取得了不错的媒体 ...