【SPOJ – REPEATS】 后缀数组【连续重复子串】
字体颜色如何
字体颜色
SPOJ - REPEATS
题意
给出一个字符串,求重复次数最多的连续重复子串。
题解
引自论文-后缀数组——处理字符串的有力工具。
“S肯定包括了字符r[0], r[L], r[L * 2],r[L * 3], ……中的某相邻的两个”
由于当前S是有两个长度为L的连续重复子串拼接而成的,那意味着S[i]和S[i+L] ( 0≤i<L )必定是一样的字符
而这两个字符位置相差L
而字符r[0],r[L],r[L * 2],r[L * 3],......中相邻两个的位置差均为L
所以只须看字符r[L* i]和r[L* (i+1)]往前和
往后各能匹配到多远,记这个总长度为K,那么这里连续出现了K/L+1次。
这句就是枚举\(r[l * i]\),\(r[l * (i+1)]\),分别作为重复子串第一二个重复的串中的字符时,重复子串的重复次数可以是多少。
结合上面图中的数组更容易理解.
如果此时r[i * L]是第一个重复子串的首字符,这样直接用公共前缀[lcp(i * L ,L* (i+1))]k除以L并向下取整+1就可以得到最后结果。但如果r[i * L]如果不是首字符,这样算完之后结果就有可能偏小,因为r[i * L]前面可能还有少许字符也能看作是第一个重复子串里的。
于是,我们不妨先算一下,从r[i * L]开始,除匹配了k/L个重复子串,还剩余了几个字符,剩余的自然是k%L个字符。如果说r[i * L]的前面还有L-k%L个字符完成匹配的话,这样就相当于利用多余的字符还可以再匹配出一个重复子串,于是我们只要检查一下从r[i * L-(L-k%L)]和r[L * (i+1)-(L-k%L)]开始是否有L-k%L个字符能够完成匹配即可,也就是说去检查这两个后缀的最长公共前缀是否比L-k%L大即可。
当然如果公共前缀不比L-k%L小,自然就不比L小,因为后面的字符都是已经匹配上的,所以为了方便编写,程序里面就直接去看是否会比L小就可以了。
代码
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define pb push_back
#define bitnum(a) __builtin_popcount(a)
//返回a中有多少个1,注意是32为无符号整数
typedef long long ll;
using namespace std;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 1e6+10;
int sa[N],cnt[N],pos[N],rk[N],oldrk[N],ht[N],n,m;
char str[N],s[2];
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;
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;
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 main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
str[i]=s[0];
}
getsa();
RMQ();
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++;
ans=max(ans,num);
}
}
printf("%d\n",ans);
}
return 0;
}
【SPOJ – REPEATS】 后缀数组【连续重复子串】的更多相关文章
- 【Poj-3693】Maximum repetition substring 后缀数组 连续重复子串
POJ - 3693 题意 SPOJ - REPEATS的进阶版,在这题的基础上输出字典序最小的重复字串. 思路 跟上题一样,先求出最长的重复次数,在求的过程中顺便纪录最多次数可能的长度. 因为sa数 ...
- SPOJ - REPEATS —— 后缀数组 重复次数最多的连续重复子串
题目链接:https://vjudge.net/problem/SPOJ-REPEATS REPEATS - Repeats no tags A string s is called an (k,l ...
- SPOJ REPEATS 后缀数组
题目链接:http://www.spoj.com/problems/REPEATS/en/ 题意:首先定义了一个字符串的重复度.即一个字符串由一个子串重复k次构成.那么最大的k即是该字符串的重复度.现 ...
- [spoj DISUBSTR]后缀数组统计不同子串个数
题目链接:https://vjudge.net/contest/70655#problem/C 后缀数组的又一神奇应用.不同子串的个数,实际上就是所有后缀的不同前缀的个数. 考虑所有的后缀按照rank ...
- POJ - 2406 ~SPOJ - REPEATS~POJ - 3693 后缀数组求解重复字串问题
POJ - 2406 题意: 给出一个字符串,要把它写成(x)n的形式,问n的最大值. 这题是求整个串的重复次数,不是重复最多次数的字串 这题很容易想到用KMP求最小循环节就没了,但是后缀数组也能写 ...
- SPOJ REPEATS Repeats (后缀数组 + RMQ:子串的最大循环节)题解
题意: 给定一个串\(s\),\(s\)必有一个最大循环节的连续子串\(ss\),问最大循环次数是多少 思路: 我们可以知道,如果一个长度为\(L\)的子串连续出现了两次及以上,那么必然会存在\(s[ ...
- POJ-3693-Maximum repetition substring(后缀数组-重复次数最多的连续重复子串)
题意: 给出一个串,求重复次数最多的连续重复子串 分析: 比较容易理解的部分就是枚举长度为L,然后看长度为L的字符串最多连续出现几次. 既然长度为L的串重复出现,那么str[0],str[l],str ...
- poj 3693 后缀数组 重复次数最多的连续重复子串
Maximum repetition substring Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8669 Acc ...
- POJ3693 Maximum repetition substring —— 后缀数组 重复次数最多的连续重复子串
题目链接:https://vjudge.net/problem/POJ-3693 Maximum repetition substring Time Limit: 1000MS Memory Li ...
随机推荐
- 学会这项python技能,就再也不怕孩子偷偷打游戏了
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:鸟哥 PS:如果想了解更多关于python的应用,可以私信小编,资料 ...
- 数据挖掘入门系列教程(九)之基于sklearn的SVM使用
目录 介绍 基于SVM对MINIST数据集进行分类 使用SVM SVM分析垃圾邮件 加载数据集 分词 构建词云 构建数据集 进行训练 交叉验证 炼丹术 总结 参考 介绍 在上一篇博客:数据挖掘入门系列 ...
- Chrome插件安利!可以一键导出微信读书笔记|支持Markdown等三种格式
众所周知,微信读书App 是一款非常优秀的阅读类App ,周围也有不少人在用.虽然工作比较忙.但是也没少在上面看书做笔记. 美中不足的是,目前微信读书虽然支持笔记导出,但是提供的是将笔记复制到剪切板, ...
- CVE 2019-0708 漏洞复现+
PART 1 参考链接:https://blog.csdn.net/qq_42184699/article/details/90754333 漏洞介绍: 当未经身份验证的攻击者使用 RDP 连接到目标 ...
- 2019CISCN华南线下两道web复现
原帖地址 : https://xz.aliyun.com/t/5558 2019CISCN华南线下的两个简单 web 部分题目下载地址,有的不完整 : 点我点我 web 1 考点 : 无参函数的 RC ...
- redis: 主从复制和哨兵模式(十三)
redis 主从复制 最低要求是一主二从(一个主机和两个从机) 主机才能写 从机只能读 只要从机连接到主机 数据就会全量复制到从机 环境配置(同一台机器) 1:配置文件 redis.conf配置如下: ...
- echarts迁徙图
前段时间在echarts社区,看见别人写的echarts迁徙图,学习并也写了一个 预览地址: https://gallery.echartsjs.com/editor.html?c=xYS-YtzOa ...
- Java 学习线路图
学习是一个循序渐进的过程,是一件非常难得坚持的事情.如果真的想学Java,一定要下定决心! 这里我分享给你的Java学习线路图,希望对你有帮助,以下为2020年更新版本,黑马君在2020年更新了Jav ...
- windows 删除非法目录
用PHP写错了路径,创建了非法目录,提示不存在该项目 复制下面 添加为批处理文件 DEL /F /A /Q \\?\%1RD /S /Q \\?\%1 将文件移入这个文件
- 总结:js世界中的特殊符号
常用符号:+ ++ - -- || / /' && 等 这些基本上每天都能用到,但是 js 世界中有些特殊符号是不常用的,我也是偶然在阅读大神代码的时候发现的,一番查找之后得出了以下结 ...