bzoj 4180: 字符串计数
Description
SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999。
他给出了一个字符串T,字符串T中有且仅有4种字符 'A', 'B', 'C', 'D'。现在他要求蒟蒻yts1999构造一个新的字符串S,构造的方法是:进行多次操作,每一次操作选择T的一个子串,将其加入S的末尾。
对于一个可构造出的字符串S,可能有多种构造方案,Oxer定义构造字符串S所需的操作次数为所有构造方案中操作次数的最小值。
Oxer想知道对于给定的正整数N和字符串T,他所能构造出的所有长度为N的字符串S中,构造所需的操作次数最大的字符串的操作次数。
蒟蒻yts1999当然不会做了,于是向你求助。
Solution
如果S字符串我们已经知道,那么求操作次数就是一个贪心的过程:因为走到后缀自动机上每一个节点的路径对应原串的一个子串,在后缀自动机上一直走,直到不可以走为止,然后重新开始匹配
基于这个思路,我们可以二分一个操作次数\(mid\),然后用 \(mid\) 个原串的子串构造一个长度最小的串\(S\),然后比较与\(n\)的关系即可
考虑构造的方法:
设 \(f[i][j]\) 表示以字符\(i\)开头的子串后面接上一个以\(j\)开头的子串,使得\(i\)开头的和\(j\)开头的两个子串接在一起不是原串的子串的情况下,\(i\)后面接的这个子串最少是多长
显然我们只需要用\(f\)数组转移\(mid\)次,取最小的一个串即可,这个过程每一步都是相同的,可以用矩阵快速幂优化或倍增\(floyd\)优化一下.
考虑预处理\(f\)数组:
设\(g[i][j]\)表示在后缀自动机上的\(i\)节点,最少走几步可以使后面接上一个以\(j\)开头的子串,接好的串不出现在原串中.
\(g[i][j]=min(g[son][j]+1)\)
\(g[i][j]=0\) 如果\(i\)节点没有\(j\)这个儿子
\(f[i][j]=g[ch[1][i]][j]\),1的\(i\)儿子往后走的串一定是以\(i\)开头的子串
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;const ll inf=1e18+10;
ll n;char S[N];int cur=1,cnt=1,len[N],fa[N],ch[N][5];
inline void ins(int c){
int p=cur;cur=++cnt;len[cur]=len[p]+1;
for(;!ch[p][c];p=fa[p])ch[p][c]=cur;
if(!p)fa[cur]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1)fa[cur]=q;
else{
int nt=++cnt;len[nt]=len[p]+1;
memcpy(ch[nt],ch[q],sizeof(ch[q]));
fa[nt]=fa[q];fa[q]=fa[cur]=nt;
for(;ch[p][c]==q;p=fa[p])ch[p][c]=nt;
}
}
}
int sa[N],c[N],f[N][6];
inline void priwork(){
memset(f,127/3,sizeof(f));
for(int i=1;i<=cnt;i++)c[len[i]]++;
for(int i=1;i<=cnt;i++)c[i]+=c[i-1];
for(int i=cnt;i;i--)sa[c[len[i]]--]=i;
for(int i=cnt;i;i--){
int x=sa[i];
for(int j=0;j<4;j++){
if(!ch[x][j])f[x][j]=1;
for(int k=0;k<4;k++)
f[x][j]=min(f[x][j],f[ch[x][k]][j]+1);
}
}
}
struct node{
ll a[5][5];
node(){memset(a,0,sizeof(a));}
void Clear(node &x,ll y){
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)x.a[i][j]=y;
}
inline node operator *(const node &p){
node ret;Clear(ret,inf);
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
for(int k=1;k<=4;k++)
ret.a[i][j]=min(ret.a[i][j],a[i][k]+p.a[k][j]);
return ret;
}
inline node ksm(node x,ll k){
node sum;
while(k){
if(k&1)sum=sum*x;
x=x*x;k>>=1;
}
return sum;
}
};
inline bool check(ll mid){
node S;
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
S.a[i][j]=f[ch[1][i-1]][j-1];
S=S.ksm(S,mid);
ll ret=inf;
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
ret=min(ret,S.a[i][j]);
return ret>=n;
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
cin>>n;
scanf("%s",S);
int le=strlen(S);
for(int i=0;i<le;i++)ins(S[i]-'A');
priwork();
ll l=1,r=n,mid,ans=0;
while(l<=r){
mid=(l+r)>>1;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans<<endl;
return 0;
}
bzoj 4180: 字符串计数的更多相关文章
- 「bzoj 4180: 字符串计数」
题目 真是一道好题 首先根据一个非常显然的贪心,如果给出了一个串\(S\),我们如何算最小操作次数呢 非常简单,我们直接把\(S\)拉到\(T\)的\(SAM\)上去跑,如果跑不动了就停下来,重新回到 ...
- BZOJ 4180: 字符串计数 后缀自动机 + 矩阵乘法 + 二分(神题)
Description SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999. 他给出了一个字符串T,字符串T中有且仅有4种字符 'A', 'B', 'C ...
- BZOJ.4180.字符串计数(后缀自动机 二分 矩阵快速幂/倍增Floyd)
题目链接 先考虑 假设S确定,使构造S操作次数最小的方案应是:对T建SAM,S在SAM上匹配,如果有S的转移就转移,否则操作数++,回到根节点继续匹配S.即每次操作一定是一次极大匹配. 简单证明:假设 ...
- 【BZOJ 4180】 4180: 字符串计数 (SAM+二分+矩阵乘法)
4180: 字符串计数 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 164 Solved: 75 Description SD有一名神犇叫做Oxe ...
- 【BZOJ-4180】字符串计数 后缀自动机 + 矩阵乘法
4180: 字符串计数 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 146 Solved: 66[Submit][Status][Discuss] ...
- BZOJ 2839: 集合计数 解题报告
BZOJ 2839: 集合计数 Description 一个有\(N\)个元素的集合有\(2^N\)个不同子集(包含空集),现在要在这\(2^N\)个集合中取出若干集合(至少一个),使得 它们的交集的 ...
- bzoj 3473 字符串 - 后缀数组 - 树状数组
题目传送门 传送门 题目大意 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串 先用奇怪的字符把所有字符串连接起来. 建后缀树,数每个节点的子树内包含多少属 ...
- BZOJ 2865 字符串识别 | 后缀数组 线段树
集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...
- BZOJ 3277 串 & BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析、启发式合并、线段树合并、主席树)
标签那么长是因为做法太多了... 题目链接: (bzoj 3277) https://www.lydsy.com/JudgeOnline/problem.php?id=3277 (bzoj 3473) ...
随机推荐
- 第1次作业:小菜鸟的平凡IT梦
#1.结缘计算机的始末 ##1.1与计算机相识的几年 作为一个95后,出生在一个互联网开始兴盛的时代.我记得小学的时候,开始知道电脑这个东西,学校有了机房,开始有了所谓的电脑课.那时候计算机对于我来说 ...
- Flask 学习 十二 用户评论
评论在数据库中的表示 由于评论和2个模型有关系,分别是谁发了评论,以及评论了哪个文章,所以这次要更新数据库模型 models.py 创建用户评论数据库模型 class Comment(db.Model ...
- 关于python爬虫经常要用到的一些Re.正则表达式
转载:https://blog.csdn.net/skyeyesxy/article/details/50837984 1.正则表达式的常用符号与方法 常用符号:点号,星号,问号与括号(小括号) (. ...
- CentOS 7 PHP-redis扩展安装,浏览器不显示数据及redis无法储存数据常见问题解决办法
首先使用php -m 可以查看到自己安装了那些扩展. 1.使用wget下载redis压缩包 wget https://github.com/phpredis/phpredis/archive/deve ...
- mysql常用命令整理
#不压缩备份 mysqldump -u root -p userpassword databasename > /tmp/backupfile.sql #压缩备份 mysqldump -u ro ...
- Centos7安装openvpn及客户端配置
1.openvpn介绍 VPN直译就是虚拟专用通道,是提供给企业之间或者个人与公司之间安全数据传输的隧道,使用OpenSSL加密库中的SSLv3/TLSv1协议函数库. 目前OpenVPN能在Sola ...
- crontab的mysqldump备份任务未能完全正确执行的故障处理
crontab是每个运维一线人员必须掌握的技术,熟练运用crontab可以自动帮助我们执行重复性的工作,提高运维的工作效率.它就像一个闹钟,在特定的时间,准时响应并执行相应的任务.如果你的工作经常与L ...
- Angular组件——组件生命周期(二)
一.view钩子 view钩子有2个,ngAfterViewInit和ngAfterViewChecked钩子. 1.实现ngAfterViewInit和ngAfterViewChecked钩子时注意 ...
- angular2 学习笔记 ( unit test 单元测试 )
第一次写单元测试. 以前一直都有听说 TDD 的事情. 今天总算是去尝试了一下. 先说说 TDD 的想法, 是这样的, 开发项目的流程 : 确定需求 -> 写类,接口,方法的名字(不写具体实现代 ...
- JDBC学习笔记 day1
JDBC的基本概念: JDBC就是java database connectivity,即java数据库连接. JDBC主要完成的几个任务分别为 与数据库建立一个连接 向数据库发送SQL语句 处理数据 ...