题意

给定字符串,求重复次数最多的连续重复子串

思路

后缀数组的神题

让我对着题解想了快1天

首先考虑一个暴力,枚举循环串的长度l,然后再枚举每个点i,用i和i+l匹配,如果匹配长度是L,这个循环串就出现了\(\lfloor\frac{L}{l}\rfloor+1\)次

但是这样显然是n^2的

根本过不去

考虑一个常见的思路,间隔某个长度设置关键点,由经过关键点的个数确定贡献,如果间隔l放置关键点,那么每个长为l的循环串应该都会经过一个关键点且没有一个循环串会经过两个关键点,然后枚举关键点,复杂度就是调和级数级别(\(O(n\log n)\))了

但是注意一种特殊情况,枚举关键点判断的是从关键点位置开头的字符串的最长长度,有可能会出现一个字符串可以向左移动x位,依然满足条件,这样的字符串显然更优,出现这种情况时,l不能整除L,应该向左移动i-(l-L%l)位再次匹配,如果匹配长度大于l,证明能多出一个循环的字符串,注意只可能多出1个,多出两个及以上的情况在前面的枚举中应该已经被计算过了

这题就解决了

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
const int MAXlog = 20;
const int MAXN = 100000;
const int INF = 0x3f3f3f3f;
struct Node{
int pos,r[2];
}x[MAXN],midx[MAXN];
int barrel[MAXN],sa[MAXN],height[MAXN],ranks[MAXN],ST[MAXN][MAXlog],n;
char s[MAXN];
int c_sort(int n,int lim){
for(int i=0;i<2;i++){
memset(barrel,0,sizeof(barrel));
for(int j=1;j<=n;j++)
barrel[x[j].r[i]]++;
for(int j=1;j<=lim;j++)
barrel[j]+=barrel[j-1];
for(int j=n;j>=1;j--)
midx[barrel[x[j].r[i]]--]=x[j];
for(int j=1;j<=n;j++)
x[j]=midx[j];
}
int cnt=1;
ranks[x[1].pos]=1;
for(int i=2;i<=n;i++)
if(x[i].r[0]==x[i-1].r[0]&&x[i].r[1]==x[i-1].r[1])
ranks[x[i].pos]=cnt;
else
ranks[x[i].pos]=++cnt;
return cnt;
}
void cal_sa(int n){
for(int i=1;i<=n;i++)
x[i]=(Node){i,s[i],0};
int cnt=c_sort(n,255);
for(int i=1;cnt<n;i<<=1){
for(int j=1;j<=n;j++)
x[j]=(Node){j,(i+j<=n)?ranks[i+j]:0,ranks[j]};
cnt=c_sort(n,cnt);
}
for(int i=1;i<=n;i++)
sa[ranks[i]]=i;
for(int i=1,j=0,k;i<=n;height[ranks[i++]]=j)
for(j?j--:0,k=sa[ranks[i]-1];s[i+j]==s[j+k];j++);
}
void init(void){
memset(sa,0,sizeof(sa));
memset(ranks,0,sizeof(ranks));
memset(height,0,sizeof(height));
memset(ST,0,sizeof(ST));
}
void init_ST(void){
for(int i=1;i<=n;i++)
ST[i][0]=height[i];
for(int i=1;i<MAXlog;i++)
for(int j=1;j<=n;j++)
ST[j][i]=min(ST[j][i-1],ST[min(j+(1<<(i-1)),n)][i-1]);
}
int LCP(int l,int r){
l=ranks[l];
r=ranks[r];
if(l==r)
return INF;
if(l>r)
swap(l,r);
l++;
int k=0;
while((1<<(k+1))<=(r-l+1))
k++;
return min(ST[l][k],ST[r-(1<<k)+1][k]);
}
signed main(){
int T;
scanf("%lld",&T);
while(T--){
init();
scanf("%lld",&n);
for(int i=1;i<=n;i++){
char c=getchar();
while(c!='a'&&c!='b')
c=getchar();
s[i]=c;
}
cal_sa(n);
// printf("ok\n");
init_ST();
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j+=i){
int midl=LCP(j,j+i);
int times=midl/i+1;
int lbe=j-i+midl%i;//j-(i-midl%i)
int midr=LCP(lbe,lbe+i);
if(midr>i)
times++;
if(times>ans)
ans=times;
}
printf("%lld\n",ans);
}
return 0;
}

SPOJ 687 REPEATS - Repeats的更多相关文章

  1. SPOJ 687 Repeats(后缀数组+ST表)

    [题目链接] http://www.spoj.com/problems/REPEATS/en/ [题目大意] 求重复次数最多的连续重复子串的长度. [题解] 考虑错位匹配,设重复部分长度为l,记s[i ...

  2. SPOJ REPEATS Repeats (后缀数组 + RMQ:子串的最大循环节)题解

    题意: 给定一个串\(s\),\(s\)必有一个最大循环节的连续子串\(ss\),问最大循环次数是多少 思路: 我们可以知道,如果一个长度为\(L\)的子串连续出现了两次及以上,那么必然会存在\(s[ ...

  3. [SPOJ 687]Repeats

    Description 题库链接 给出一个长度为 \(n\) 的字符串,求重复次数最多的连续重复子串. \(1\leq n\leq 50000\) Solution Code #include < ...

  4. SPOJ - REPEATS Repeats (后缀数组)

    A string s is called an (k,l)-repeat if s is obtained by concatenating k>=1 times some seed strin ...

  5. SPOJ - REPEATS Repeats (后缀数组+RMQ)

    题意:求一个串中出现重复子串次数最多的数目. 析:枚举每个长度的子串,至少要重复两次,必然会经过s[l*i]中相邻的两个,然后再分别向前和向后匹配即可. 代码如下: #pragma comment(l ...

  6. spoj687 REPEATS - Repeats (后缀数组+rmq)

    A string s is called an (k,l)-repeat if s is obtained by concatenating k>=1 times some seed strin ...

  7. SP687 REPEATS - Repeats

    给定字符串,求重复次数最多的连续重复子串. 题目很简单,被细节坑惨了... 前置的一个推论:请看这里. #include <bits/stdc++.h> using namespace s ...

  8. SP687 REPEATS - Repeats(后缀数组)

    一个初步的想法是我们枚举重复子串的长度\(L\).然后跑一遍SA.然后我们枚举一个点\(i\),令他的对应点为\(i+L\),然后求出这两个点的LCP和LCS的长度答案就是这个点的答案就是\((len ...

  9. 题解 SP687 【REPEATS - Repeats】

    考虑可以枚举字符串上的两个点,求出两个点所对应后缀的\(LCP\)和所对应前缀的\(LCS\),两点之间的距离为\(len\),则这两个点对答案的贡献为: \[ \frac{LCS+LCP+L-1}{ ...

随机推荐

  1. Eclipse修改编码方式

    1.修改工作空间默认编码 1.修改工作空间默认编码:window -> preferences ->  General -> Workspace 2.修改文件的编码 在Eclipse ...

  2. hdu3879 最大权闭合回路

    题意: 有n个基站可以建立,然后m个团体会使用这些基站进行工作,地i个团体会适应Ai Bi 这两个基站, 如果建成收益Ci,  第j个基站花费Pj,求如何建立使得收益最大, 将每个团体看以一个点,然后 ...

  3. 即时通讯(I)

    网络通讯三要素: 网络七层协议划分: 网络五层协议的划分: 要记网络层的5层协议,可以把它想像为一枚洋葱.学过计算机网络的,看到这个网络协议的套接字,大概就会明白了!它是一层一层的进行包裹的,然后交由 ...

  4. Java注解的原理

    自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分.开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解.这篇文章中,我将向大家讲述 ...

  5. Axis2之wsdl2java工具

    本章主要介绍axis2的wsdl2java工具的使用. Axis2提供了一个wsdl2java命令可以根据WSDL文件自动产生调用WebService的代码.wsdl2java命令可以在<Axi ...

  6. c# ref和out参数

    向方法传递参的时候,对应的参数通常会用实参的拷贝来初始化.就是说随便在方法内部进行怎样的修改,都不会影响作为参数传递的变量的原始值. 通过上面的例子我们可以看出来,如果一个方法的参数是引用类型,那么使 ...

  7. centos 6.8 配置csh的shell和环境变量

    1.查看shell 查看系统中安装的所有版本的shell:cat   /etc/shells 查看当前用户使用的shell:echo $SHELL 2.修改用户shell 可以在/etc/passwd ...

  8. jumpserver堡垒机安装

    1. 下载jumpserver cd /opt wget https://github.com/jumpserver/jumpserver/archive/master.zip unzip maste ...

  9. Tomcat启动程序端口冲突、确认相应进程及杀死冲突进程的解决方案

    一. 查看所有进程占用的端口 在开始-运行-cmd,输入:netstat –ano可以查看所有进程 二.查看占用指定端口的程序(1)命令窗口输出 命令:netstat –ano | findstr & ...

  10. Vue小案例 之 商品管理------创建页面与部分数据

    logo的路径: 页面的初始布局: 初始的HTML: <div id="container"> <!--logo title--> <div clas ...