CF1968G2.Division+LCP-字符串、Z函数

news/发布时间2024/5/17 17:09:46

link:https://codeforces.com/problemset/problem/1968/G2
给一个字符串 \(s\),定义 \(f_k\) 表示:将 \(s\) 划分成恰好 \(k\)\(w_1,\dots,w_k\) 之后, \(LCP(w_1,\dots,w_k)\)$ 的最大值,其中LCP表示最长公共前缀。求 \(f_l,\dots,f_r\) (其实和求所有 \(f\) 没什么区别)
\(\sum |s|\leq 2\times 10^5\).


对于这样的划分问题,首先需要注意到的(显然的事实)是: \(s\)前缀一定会作为某个 \(w_i\) 出现,因此LCP也一定是 \(s\) 的前缀。

那么就可以想,暴力枚举 \(s\) 的前缀 \(s[1,\dots,k]\),如果其至多能在 \(s\) 当中不相交地出现 \(c\) 次,那么 \(f_1,\dots,f_c\) 都至少是 \(k\).
判断 \(s\) 前缀在 \(s\) 中不相交地出现几次,直接用KMP是稳定 \(O(n)\) 的,对每个 \(k\) 处理就退化成 \(O(n^2)\) 了。
难道会是什么神奇的后缀结构吗(并不会后缀xxx系列…),可恶,难道就到此为止了吗…

不可以,div3不会是这样的难度,让我们来想点稍微简单的东西,KMP麻烦就麻烦在要把整个串跑一遍,而我们知道枚举前缀 \(k\) 的话,至多有 \(O(n/k)\) 段,这里其实差了很多,也许可以尝试从这里想。如果写哈希的话,要怎么从当前一段快速跳到下一段,每次查询的前缀都不一样,用哈希也只能把整个串扫一遍…

等一下!Z函数是求什么来着的? \(z_i\) 能处理 \(s\)\(s[i,\dots,n]\) 的LCP,如果用Z函数来跳,假设当前在位置 \(i\),那只要在 \([i+1,n]\) 当中找到最小的 \(j\) 使得 \(z_j\geq k\) ,然后把 \(i\) 跳到 \(j\) 就可以了,寻找 \(j\) 的过程可以用静态RMQ+二分完成,这样复杂度就是 \(O(\log n\times (n/k))\) 了,对每个 \(k\) 跑一遍,算出出现次数 \(c\) ,然后给 \(f_1,\dots f_c\) 做一个取max的操作,前缀取max也可用类似前缀和那样通过打标记完成。

总复杂度 \(O(n\log^2 n)\).

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N=2e5+5;
vector<int> exKMP(const string &s){int n=s.length();vector<int> z(n);for(int i=1,l=0,r=0;i<n;++i){if(i<=r&&z[i-l]<r-i+1)z[i]=z[i-l];else{z[i]=max(0,r-i+1);while(i+z[i]<n&&s[z[i]]==s[i+z[i]])++z[i];}if(i+z[i]-1>r)l=i,r=i+z[i]-1;}return z;
}int n,l,r,tag[N];
int f[21][N],Log[N];
string s;
int query(int l,int r){int k=Log[r-l+1];return max(f[k][l],f[k][r-(1<<k)+1]);
}
int main(){fastio;Log[0]=-1;rep(i,1,N-5)Log[i]=Log[i>>1]+1;int tc;cin>>tc;while(tc--){cin>>n>>l>>r>>s;auto z=exKMP(s);rep(i,0,n-1)f[0][i]=z[i];rep(i,0,n)tag[i]=0;rep(j,1,20)for(int i=0;i+(1<<j)-1<n;i++)f[j][i]=max(f[j-1][i],f[j-1][i+(1<<(j-1))]);auto work=[&](int k){int cnt=1;for(int i=0;i<n;){int L=i+k,R=n-1,ret=-1;while(L<=R){int mid=(L+R)>>1;if(query(i+k,mid)>=k){ret=mid;R=mid-1;}else L=mid+1;}if(ret==-1)break;cnt++;i=ret;}tag[cnt]=max(tag[cnt],k);};rep(i,1,n)work(i);tag[n+1]=0;for(int i=n;i>=1;i--)tag[i]=max(tag[i],tag[i+1]);rep(i,l,r)cout<<tag[i]<<' ';cout<<endl;}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ulsteruni.cn/article/00248400.html

如若内容造成侵权/违法违规/事实不符,请联系编程大学网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

认知提升的方法

认知提升的方法一、什么是认知 经验是对于过往经历的总结归纳,当把这种经验传授给别人时,这种经验对别人来说就是知识。所以,知识是人脑对客观事物的信息沉淀。 技能是人们通过练习而获得的动作方式和系统,例如操作技能中的PS技术、木工技术、电工技术、水工技术等,而能力…

将社会脆弱性纳入高分辨率全球洪水风险绘图

贡献 将高分辨率流洪水模型的年平均超标概率估计值与网格化人口和贫困数据相结合,创建了 90 米分辨率的全球洪水脆弱性调整风险指数(VARI Flood)。该指数提供了国家内部或国家之间相对风险的估计值,并通过识别以高密度和高社会脆弱性为特征的 "热点地区",改变了…

acwing351

https://www.acwing.com/activity/content/problem/content/9051/ NOIP2007提高组T4。本题是加强版。 题目描述 设 \(T=(V, E, W)\) 是一个无圈且连通的无向图(也称为无根树),每条边带有正整数的权,我们称 \(T\) 为树网(treenetwork),其中 \(V, E\) 分别表示结点与边的集…

Unity热更学习笔记--AB包的依赖 0.98

AB包的依赖 接上一小结。 在这里我们新建一个红色材质球,赋值给Cube预制体。此时不对材质球进行AB包分类,再次进行打包。运行脚本,发现红色cube成功的从AB包中加载出来。尽管我们没有将cube所依赖的材质球进行打包分类,但是打包时候unity会自动将包中的物体相关依赖打入包中…

Y2 知识和题单

Link。 0x01 进制 引入 计数原理,对于 \(N\) 进制,那么就是逢 \(N\) 进一。 计算机中常用二进制,对应电路中的通电(\(1\))断电(\(0\))。 人类从远古以来使用十进制。 常用的有二进制、三进制、八进制、十进制、十六进制等。 由于不同进制之间数值写法可能相同,在没有特…

Clock Switch,芯片时钟切换的毛刺是什么,如何消除

背景 芯片运行过程中需要时钟切换时,要考虑到是否会产生glitch,小小的glitch有可能导致电路运行的错误。所以时钟切换时需要特别的处理。 直接使用MUX进行时钟切换或者采用如下电路结构进行时钟切换:assign outclock = (clk1 & select) | (~select & clk0);或 assig…