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. golang slice 源码解读

    本文从源码角度学习 golang slice 的创建.扩容,深拷贝的实现. 内部数据结构 slice 仅有三个字段,其中array 是保存数据的部分,len 字段为长度,cap 为容量. type s ...

  2. jQuer实时监控input对table进行筛选

    记得以前写过一个预定表格~~~~~比这个更难,一大串前端js~~~忘了~~~好记性不如烂笔头~~记录下,既帮助别人,也帮助自己~~~ 实现思路~通过.on监听input标签的内容变化,通过this获取 ...

  3. 纯js时钟特效详细代码分析实例教程

    电子时钟是网上常见的功能,在学习date对象和定时器功能时,来完成一个电子时钟的制作是不错的选择.学习本教程之前,读者需要具备html和css技能,同时需要有简单的javascript基础. 先准备一 ...

  4. 功能测试--聊天功能测试&微信聊天

    微信聊天功能测试 发送对象 普通用户.公众号.群.其他特殊主体 衍生功能 转发.语音转文字.删除等 消息发送 单聊.群聊.语音.文字.图片.表情.链接.字符及长度 消息管理 发布通知.接受通知.发文件 ...

  5. 全网最全最细的jmeter接口测试教程以及接口测试流程详解

    一.Jmeter简介 ​ Jmeter是由Apache公司开发的一个纯Java的开源项目,即可以用于做接口测试也可以用于做性能测试. Jmeter具备高移植性,可以实现跨平台运行. Jmeter可以实 ...

  6. 浅析Java7中的ConcurrentHashMap

    引入ConcurrentHashMap 模拟使用hashmap在多线程场景下发生线程不安全现象 import java.util.HashMap; import java.util.Map; impo ...

  7. Vue【你知道吗?】

    前言 Vue的由来 Vue最早发布于2014年左右,作者是美中国学生尤雨溪.Vue 的定位就是为前端开发提供一个低门槛,高效率,但同时又能够伴随用户成长的框架 尤雨溪谈Vue.js :缔造自由与真我 ...

  8. python 利用numpy同时打乱列表的顺序,同时打乱数据和标签的顺序

    可用于网络训练打乱训练数据个标签,不改变对应关系 方法一: np.random.shuffle (无返回值,直接打乱原列表) state = np.random.get_state() np.rand ...

  9. 关于Sysinternals Suite

    sysinternals 的网站创立于1996年由Mark russinovich和布赖科格斯韦尔主办其先进的系统工具和技术资料·微软于2006年7月收购sysinternals公司 . 不管你是一个 ...

  10. Windows API 中 OVERLAPPED 结构体 初始化

    出处:https://github.com/microsoft/Windows-classic-samples/blob/1d363ff4bd17d8e20415b92e2ee989d615cc0d9 ...