\(\color{white}{mjt是机房模拟赛独自切过题的唯一的人...}\)
(应本人要求删掉惹)


\(Description\)

给你\(n,k\)和长为\(n\)的字符串\(s\)。一个区间\([l,r]\)是合法的,当且仅当\(s[l...r]\)能被分成\(k\)个相同的子串。求有多少个合法区间。
\(n,k\leq3\times10^5\)。

\(Solution\)

枚举单个子串的长度\(len\),在\(s\)上从\(1\)开始每隔\(len\)个位置放一个关键点,分成若干块。
考虑相邻\(k\)个关键点,以它们开头的\(k\)个子串是否相同。如果它们两两之间的\(LCP\)都\(\geq len\),显然是合法的。考虑怎么求这\(k\)个位置的\(LCP\)。
建\(SA\),\(LCP\)是两两\(rk\)之间的最小值,也就是区间\([\min\{rk\},\max\{rk\}]\)之间的最小值。用\(set\)动态维护一下,查询\(LCP\)就是\(O(1)\)的了。
需要枚举\(O(n\log n)\)次,这样这部分复杂度是\(O(n\log^2n)\)。

还有左右端点在块内的情况,也就是跨过了\(k-1\)个整块。容易发现这\(k-1\)块的子串一定需要是相同的。同样用\(SA\)和\(set\)先判一下。
设左端点\(l\)在第\(p\)块内,右端点\(r\)在\(p+k\)块内。记\(L[p],R[p]\)分别为第\(p\)块的左右端点,可以发现合法的\(l\)范围是前缀\(R[p]\)与\(R[p+1]\)的最长公共后缀,可以反着建个\(SA\)求出来,设为\(a\)。同理合法\(r\)的范围是后缀\(L[p+k]\)与\(L[p+k-1]\)的最长公共前缀,设为\(b\)(注意与\(len-1\)取\(\min\))。
\(r\)的长度需要在\([len-a,len-1]\)之间,所以此时合法的区间个数就是\(b-len+a+1\)。
复杂度也是\(O(n\log^2n)\)。

ps: 不知道标算是啥...(好像是SAM+主席树)这是\(\color{red}{\text{m}}\color{black}{\text{jt}}\)的做法。
这个其实和优秀的拆分差不多...但是考场都忘了啊=-=


#include <set>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rg register
#define Rev(x) (n-(x)+1)
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=3e5+5;int s[N],Log[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Suffix_Array
{int sa[N],rk[N],sa2[N],tm[N],ht[N],mn[19][N];inline int LCP(int l,int r){l=rk[l], r=rk[r]; if(l>r) std::swap(l,r);++l; int k=Log[r-l+1];return std::min(mn[k][l],mn[k][r-(1<<k)+1]);}inline int LCP2(int l,int r){if(l==r) return N;++l; int k=Log[r-l+1];return std::min(mn[k][l],mn[k][r-(1<<k)+1]);}void Build(int *s,int n){int *x=rk,*y=sa2,m=27;for(int i=0; i<=m; ++i) tm[i]=0;for(int i=1; i<=n; ++i) ++tm[x[i]=s[i]+1];for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];for(int i=n; i; --i) sa[tm[x[i]]--]=i;for(int k=1,p=0; k<n; k<<=1,m=p,p=0){for(int i=n-k+1; i<=n; ++i) y[++p]=i;for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;for(int i=0; i<=m; ++i) tm[i]=0;for(int i=1; i<=n; ++i) ++tm[x[i]];for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];std::swap(x,y), x[sa[1]]=p=1;for(int i=2; i<=n; ++i)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;if(p>=n) break;}for(int i=1; i<=n; ++i) rk[sa[i]]=i;ht[1]=0;for(int i=1,k=0; i<=n; ++i){if(rk[i]==1) continue;if(k) --k;int p=sa[rk[i]-1];while(i+k<=n && p+k<=n && s[i+k]==s[p+k]) ++k;ht[rk[i]]=k;}for(int i=1; i<=n; ++i) mn[0][i]=ht[i];for(int j=1; j<=Log[n]; ++j)for(int t=1<<j-1,i=n-t; i; --i)mn[j][i]=std::min(mn[j-1][i],mn[j-1][i+t]);}
}sa1,sa2;inline int read()
{int now=0;register char c=gc();for(;!isdigit(c);c=gc());for(;isdigit(c);now=now*10+c-48,c=gc());return now;
}int main()
{freopen("sutoringu.in","r",stdin);freopen("sutoringu.out","w",stdout);const int n=read(),K=read();register char c=gc(); while(!isalpha(c)) c=gc(); s[1]=c-'a';for(rg int i=2; i<=n; ++i) s[i]=gc()-'a', Log[i]=Log[i>>1]+1;sa1.Build(s,n), std::reverse(s+1,s+1+n), sa2.Build(s,n);LL ans=0;for(int len=1; len*K<=n; ++len){std::set<int> st;//直接新开一个 for(int t=K-1,i=1; t--; i+=len) st.insert(sa1.rk[i]);for(int t=(K-1)*len,i=t+1; i+len-1<=n; i+=len){st.insert(sa1.rk[i]);ans+=sa1.LCP2(*st.begin(),*st.rbegin())>=len;st.erase(sa1.rk[i-t]);}if(len==1) continue;std::set<int> st2;for(int t=K-1,i=len+1; t--; i+=len) st2.insert(sa1.rk[i]);for(int i=K*len+1,j=len; i<=n; i+=len,j+=len){if(sa1.LCP2(*st2.begin(),*st2.rbegin())>=len){int a=std::min(sa2.LCP(Rev(j),Rev(j+len)),len-1),b=std::min(sa1.LCP(i,i-len),len-1);//rev!ans+=std::max(0,b-len+a+1);}st2.erase(sa1.rk[j+1]), st2.insert(sa1.rk[i]);}}printf("%lld\n",ans);return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/10613052.html

3.27模拟赛 sutoringu(后缀数组)相关推荐

  1. Comet OJ - 2019国庆欢乐赛 G 后缀数组

    题目链接: https://www.cometoj.com/contest/68/problem/G?problem_id=3940 出题人给的题解: 我们知道,一个子串是字符串的后缀的前缀.  这就 ...

  2. 模拟赛 sutoringu

    sutoringu 题意: 询问有多少一个字符串内有多少个个子区间,满足可以分成k个相同的串. 分析: 首先可以枚举一个长度len,表示分成的k个长为len的串.然后从1开始,每len的长度分成一块, ...

  3. 10.27模拟赛T2

    愚蠢的朴素dp比较好写,注意到唯一的瓶颈就在于状态太大,考虑离散 不能整除k的因子可以过滤掉,然后把因子放进map里面,reverse_iterator遍历一遍用朴素dp方程 dp[i][j]=dp[ ...

  4. 洛谷 P5594 【XR-4】模拟赛 视频讲解(二维数组、模拟)

    洛谷 P5594 [XR-4]模拟赛(需要 二维数组) 题目描述 X 校正在进行 CSP 前的校内集训. 一共有 nn 名 OIer 参与这次集训,教练为他们精心准备了 mm 套模拟赛题. 然而,每名 ...

  5. 用vis数组保证只入队一次P5594 【XR-4】模拟赛

    文章目录 [XR-4]模拟赛 题目描述 输入格式 输出格式 样例 #1 样例输入 #1 样例输出 #1 样例 #2 样例输入 #2 样例输出 #2 样例 #3 样例输入 #3 样例输出 #3 提示 小 ...

  6. [2020.11.26NOIP模拟赛]勇者的后缀【SA,RMQ,主席树,二分】

    正题 题目链接:https://www.luogu.com.cn/problem/U142356?contestId=37784 题目大意 一个字符串,询问给出(x,l,r)(x,l,r)(x,l,r ...

  7. [2020.10.30NOIP模拟赛]字符串水题【SA,树状数组】

    正题 题目大意 一个字符串SSS. 若干个询问,每次询问一个串TTT和l,rl,rl,r.询问有多少个TTT和SSS的公共子串满足和为[l,r][l,r][l,r] 解题思路 考虑枚举子串左端,那么右 ...

  8. ssl模拟赛(2019.4.27)

    成绩 rank是有算其他大爷的 rank name score T1 T2 T3 T4 1 L Y F LYF LYF 320 100 100 100 20 2 H K Y HKY HKY 296 1 ...

  9. 2020年 第11届 蓝桥杯 第2次模拟赛真题详解及小结【Java版】

    蓝桥杯 Java B组 省赛真题详解及小结汇总[2013年(第4届)~2020年(第11届)] 注意:部分代码及程序 源自 蓝桥杯 官网视频(历年真题解析) 郑未老师. 2013年 第04届 蓝桥杯 ...

最新文章

  1. python画散点图分布-python画时间序列散点图
  2. 登陆模块防止恶意用户客户端攻击
  3. HTTP 协议 Host 请求头的作用
  4. linux内核组件分析之--设备驱动模型之bus
  5. question regarding UI configuration type
  6. SQL Server 2008 基础
  7. linux 通过虚拟ip出路由器,linux模拟路由器实验
  8. 奇门遁甲鸣法 第五章 辅格
  9. 在windows中使用scp命令将文件上传到远端服务器
  10. 【android-音视频】listview中播放音频,实现音频时长的倒计时,暂停,切换。
  11. 估计理论(7):应用BLUE的两个例子
  12. 凯撒/摩斯/栅栏/维吉尼亚/元音密码加解密的Python实现
  13. 用python输出杨辉三角形,python输出杨辉三角
  14. 8.1 向量及其线性运算
  15. 安装redhat系统步骤图解_用u盘安装redhat图文教程
  16. 深度学习领域专有名词的理解与释义
  17. 游戏机器人的开发工具及开发步骤
  18. python实现绘制函数图像
  19. 爬虫跳过https安全认证
  20. 点餐小程序系统/基于微信小程序的点餐系统/点餐平台网站

热门文章

  1. SAP UI5框架中,onInit方法执行次数的讨论
  2. MongoDB最简单的入门教程之四:使用Spring Boot操作MongoDB
  3. 为什么ABAP整型的1转成string之后,后面会多个空格 1
  4. 动态规划简单应用:斐波那契数列
  5. apex 查询_加速器关联好司机下载器下载的apex
  6. gateway sentinel 熔断 不起作用_Spring Cloud Alibaba集训营第五天(服务熔断和限流)
  7. html判断安装没安装qq,QQ6.1体验版怎么用?腾讯QQ6.1体验版本安装步骤(无须申请体验账号)...
  8. 准考证打印系统关闭怎么办_初级会计准考证无法打印受限制怎么办?学姐帮你解答相关问题...
  9. java红包雨_Java升职加薪课开发企业年会红包雨场景项目实战视频教程
  10. 查询mysql视图_MySQL数据库简介及常用命令