洛谷P5341 [TJOI2019]甲苯先生和大中锋的字符串
原题链接P5341 [TJOI2019]甲苯先生和大中锋的字符串
题目描述
大中锋有一个长度为 n 的字符串,他只知道其中的一个子串是祖上传下来的宝藏的密码。但是由于字符串很长,大中锋很难将这些子串一一尝试。
这天大中锋找到甲苯先生算命,但是甲苯先生说:“天机不可泄漏”。
在大中锋的苦苦哀求下,甲苯先生告诉大中锋:“密码是在字符串中恰好出现了 kk 次的子串”。
但是大中锋不知道该怎么做,在大中锋再三的恳求下,甲苯先生看其真诚,又告诉他:“在恰好出现了 k 次的子串中,你去按照字串的长度分类,密码就在数量最多的那一类里”。
大中锋为了尝试这个密码,想让你帮忙找出子串长度出现次数最多的长度数(如果有多个输出最长长度)。
输入输出格式
输入格式:
第一行一个正整数 T ,表示有 T 组测试数据。
接下来 T 行每行包含一个字符串和一个正整数 k 。
输出格式:
一共输出 T 行,每行一个整数表示在出现 k 次的子串中出现次数的最多的长度。如果不存在子串出现 k 次,则输出 −1 。
输入输出样例
说明
数据说明
对于第一个数据:其中子串 b,aa,ab,aab 均只出现一次,其中长度为 1 的子串现了 1 次,长度为 2 的子串出现了 2 次,长度为 3 的子串出现了 1 次。所以答案为 2 。
对于第二个数据:其中子串 a, b, c, ab, bc, abc 均只出现一次,其中长度为 1 的子串出现了 3 次,长度为 2 的子串出现了 2 次,长度为 3 的子串出现了 1 次。所以答案为 1 。
对于第三个数据:其中子串 aaa 出现二次,长度为 3 的子串出现了 1 次,其他长度均没有。所以答案为 3 。
对于第四个数据:其中子串 a, b, ab 出现二次,其中长度为 1 的子串出现了 2 次,长度为 2 的子串出现了 1次。所以答案为 1 。
对于第五个数据:其中子串 b, c, ab, ba 出现二次,其中长度为 1 的子串出现了 2 次,长度为 2 的子串出现了 2次。所以答案为 2 。
对于第六个数据:其中子串没有出现四次。所以本题的本题的答案为 −1 。
数据范围
对于 20% 的数据, 1≤k≤n≤10
对于 100% 的数据, 1≤n≤105,1≤T≤100,∑n≤3∗106 ,输入的字符串中仅包含小写英文字母。
题解
题意概括:给定一个字符串和整数k,求在其中出现次数为k的子串中数量最多的长度
(如果长度为i的出现为k次的子串有ans个,且任意j<i满足长度为j的出现次数为k的子串数量≤ans,j>i则<ans)
算法:统计子串(数量)问题,很容易想到后缀自动机SAM,后缀数组SA
此处介绍SAM的做法,实现很简单,代码接近于模板
实现:
1.初始化,按照原字符串建立SAM,建立后缀树,递归统计子串数量siz
2.核心代码:统计每一种长度的出现次数为k的子串的数量(“子串的数量”为不同子串的种类数)
定义ans数组,ans[i]表示长度为i的出现次数为k的子串的数量,ans[x]max=ans[i]则i即为所求
如何求ans数组?
对于每个状态,如果它的siz(代表的子串的出现次数)为k,则其代表的所有子串为所求,故可按长度统计入ans
那状态i代表的子串的长度又几何?
根据后缀自动机的性质,令len[i]为状态i表示的最长的子串str[i]的长度,则
①状态i表示的所有子串为str[i]连续的后缀
②状态i的后缀连接指向的状态link[i]表示的所有子串为str[i]的后缀
③len[link[i]]+1等于i表示的最短的子串的长度
所以状态i对ans的贡献即为对于x|len[link[i]]+1≤x≤len[i],ans[x]++;
故可设ans为前缀和数组,将ans[len[link[i]]+1]++,ans[len[i]+1]--;
以下代码可在递归时操作,亦可另起一个循环
if(x&&siz[x]==K){
ans[len[link[x]]+]++;
ans[len[x]+]--;
}
完整代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+,MAXN=2e5+,MAXC=;
int nxt[MAXN][MAXC],link[MAXN],len[MAXN],lst,sz,siz[MAXN];
inline void extend(int c){
int cur=++sz,p=lst;
len[cur]=len[p]+;
siz[cur]=;
while(p!=-&&!nxt[p][c]){
nxt[p][c]=cur;
p=link[p];
}
if(p==-)
link[cur]=;
else{
int q=nxt[p][c];
if(len[q]==len[p]+)
link[cur]=q;
else{
int clone=++sz;
len[clone]=len[p]+;
memcpy(nxt[clone],nxt[q],sizeof(nxt[clone]));
link[clone]=link[q];
while(p!=-&&nxt[p][c]==q){
nxt[p][c]=clone;
p=link[p];
}
link[q]=link[cur]=clone;
}
}
lst=cur;
}
int tp,head[MAXN],to[MAXN],nxt_[MAXN];
inline void add(int x,int y){
nxt_[++tp]=head[x];
head[x]=tp;
to[tp]=y;
}
int K,ans[MAXN];
void dfs(int x){
for(int i=head[x];i;i=nxt_[i]){
dfs(to[i]);
siz[x]+=siz[to[i]];
}
if(x&&siz[x]==K){
ans[len[link[x]]+]++;
ans[len[x]+]--;
}
}
char str[MAXN];
int M;
int Case;
int main(){
scanf("%d",&Case);
while(Case--){
lst=sz=tp=;
link[]=-;
scanf("%s%d",str+,&K);
M=strlen(str+);
for(int i=;i<=M;i++)
extend(str[i]-'a'),siz[lst]=;
for(int i=;i<=sz;i++)
add(link[i],i);
dfs();
for(int i=;i<=M;i++)
ans[i]+=ans[i-];
int maxi=;
for(int i=M;i>=;i--)
if(ans[i]>ans[maxi])
maxi=i;
printf("%d\n",maxi?maxi:-);
for(int i=;i<=sz;i++){
len[i]=siz[i]=link[i]=;
}
memset(head,,sizeof(head));
memset(ans,,sizeof(ans));
memset(nxt,,sizeof(nxt));
}
return ;
}
洛谷P5341 [TJOI2019]甲苯先生和大中锋的字符串的更多相关文章
- 【题解】Luogu P5341 [TJOI2019]甲苯先生和大中锋的字符串
原题传送门 实际按照题意模拟就行 我们先求出字符串的sa 因为要在字符串中出现k次,所以我们枚举\(l,r(r-l+1=k)\)看一共有多少种合法的方案 合法方案的长度下界\(lb\)为\(Max(h ...
- luogu P5341 [TJOI2019]甲苯先生和大中锋的字符串
传送门 考虑子串以及出现个数,可以发现SAM可以快速知道每种子串的出现次数,即所在状态的\(endpos\)集合大小,然后一个状态对应的子串长度是一段连续区间,所以可以对每个状态差分一下,就能统计答案 ...
- p5341 [TJOI2019]甲苯先生和大中锋的字符串
分析 TJOI白给题 建出sam,对于每个点如果它的子树siz和等于k 那么对于这个满足的点它有贡献的长度一定是一个连续区间 直接差分即可 代码 #include<bits/stdc++.h&g ...
- [TJOI2019]甲苯先生和大中锋的字符串——后缀自动机+差分
题目链接: [TJOI2019]甲苯先生和大中锋的字符串 对原串建后缀自动机并维护$parent$树上每个点的子树大小,显然子树大小为$k$的节点所代表的子串出现过$k$次,那么我们需要将$[len[ ...
- 【洛谷 P5341】 [TJOI2019]甲苯先生和大中锋的字符串(后缀自动机)
题目链接 建出\(sam\),求出parent tree上每个点的\(endpos\)集合大小. 如果等于\(k\),说明到达这个点的都可以.给\((len[fa(i)],len[i]]\)的\(cn ...
- Tjoi2019 甲苯先生和大中锋的字符串 后缀自动机_差分
tjoi胆子好大,直接出了两道送分题...... 都 9102 年了,还有省选出模板题QAQ...... Code: #include <bits/stdc++.h> #define se ...
- [TJOI2019]甲苯先生和大中锋的字符串
有个叫asuldb的神仙来嘲讽我 说这题SAM水题,而且SA过不了 然后我就用SA过了 显然是一个Height数组上长为k的滑块,判一下两边,差分一下就可以了 #include"cstdio ...
- 洛谷P5338 [TJOI2019]甲苯先生的滚榜
原题链接洛谷P5338 [TJOI2019]甲苯先生的滚榜 题目描述 甲苯先生在制作一个online judge,他发现做比赛的人们很关心自己的排名(显而易见),在acm赛制的比赛中,如果通过题目数量 ...
- 洛谷P3502 [POI2010]CHO-Hamsters感想及题解(图论+字符串+矩阵加速$dp\&Floyd$)
洛谷P3502 [POI2010]CHO-Hamsters感想及题解(图论+字符串+矩阵加速\(dp\&Floyd\)) 标签:题解 阅读体验:https://zybuluo.com/Junl ...
随机推荐
- ICPC 2018 焦作区域赛
// 2019.10.7 练习赛 // 赛题来源:2018 ICPC 焦作区域赛 // CF链接:http://codeforces.com/gym/102028 A Xu Xiake in Hena ...
- SimpleDateFormat日期格式
前言 java中使用SimpleDateFormat类的构造函数SimpleDateFormat(String str)构造格式化日期的格式,通过format(Date date)方法将指定的日期对象 ...
- 面向XX程序设计到底是个啥
面向过程编程:面向(对着)-->过程(流程步骤)-->编程(码代码) IPO是啥 input(输入)-->process(过程处理)-->output(输出) 未来码代码的目的 ...
- iOS进阶一OC对象的本质
OC对象的本质 平时编写的Object-C代码,底层实现其实都是C/C++代码. 所以Objective-C的面向对象都是基于C/C++的数据结构实现的,OC对象内部可以容纳不同数据类型的数据,因此可 ...
- nodejs . module.exports
//utils.js let a = 100; console.log(module.exports); //能打印出结果为:{} console.log(exports); //能打印出结果为:{} ...
- lnmp高人笔记
http://www.cnblogs.com/qizekai/p/5878774.html http://www.cnblogs.com/qizekai/p/5879461.html
- bzoj 3579: 破冰派对
题意: 给你一个图,问你有多少个方案把他分成连个新的图.使得一个图是一个团,另外一个是独立集 一些闲话: 以前做过一次这个题..当时听说爆搜可以过,就无脑莽过去了.. 也没有思考为什么爆搜能过,或者有 ...
- Android开发 多媒体提取器MediaExtractor详解_将一个视频文件分离视频与音频
前言 此篇博客讲解MediaExtractor将一个视频文件分离视频与音频,如果你对MediaExtractor还没有一个笼统的概念建议先了解我的另一篇入门博客:https://www.cnblogs ...
- LUOGU P2290 [HNOI2004]树的计数(组合数,prufer序)
传送门 解题思路 \(prufer\)序,就是所有的不同的无根树,都可以转化为唯一的序列.做法就是每次从度数为\(1\)的点中选出一个字典序最小的,把这个点删掉,并把这个点相连的节点加入序列,直到只剩 ...
- (转)第04节:Fabric.js用路径画不规则图形
在Canvas上画方形.圆形.三角形都是很容易的,只要调用fabric对应的方法就可以了,但这些都是规则的图形,如果你想画一个不规则的图形,这时候你可以用fabric.js提供的路径绘图方法.所谓路径 ...