BZOJ1396 识别子串【SAM+SegmentTree】
BZOJ1396 识别子串
给定一个串\(s\),对于串中的每个位置,输出经过这个位置且只在\(s\)中出现一次的子串的最短长度
朴素的想法是,我们要找到那些只出现一次的子串,之后遍历每个串,把串所覆盖的区域区间和串长取\(min\)
考虑优化,根据\(s\)串先建立\(SAM\),然后计算出每个状态的\(endpos\)集合的大小,其中大小为\(1\)的状态所表示的一系列子串必然只在原串中出现一次,对于\(endpos\)大小为\(1\)的某个状态\(u\),其表示的子串的最短长度为\(len_{link_u}+1\),最长长度为\(len_u\),假设子串结束的位置为\(firstpos_u\),那么对于\([firstpos_u-len_{link_u}+1,firstpos_u]\)这段区间,需要和\(len_{link_u}\)取\(min\),而对于区间\([firstpos_u-len_u+1,firstpos_u-len_{link_u}]\)来说,区间上的每个位置\(i\)要和\(firstpos_u-i+1\)取\(min\),可以在更新的时候只考虑\(firstpos_u\)的贡献,最后计算的时候在减去\(i-1\)即可,所以根据上述方法,需要建立两棵线段树来维护,其中区间取\(min\)可以通过先排序然后直接赋值来解决
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e5+7;
char s[MAXN];
struct SegmentTree{
int lazy[MAXN<<2],l[MAXN<<2],r[MAXN<<2];
#define ls(rt) rt << 1
#define rs(rt) rt << 1 | 1
void pushdown(int rt){
if(!lazy[rt]) return;
lazy[ls(rt)] = lazy[rt]; lazy[rs(rt)] = lazy[rt];
lazy[rt] = 0;
}
void build(int L, int R, int rt = 1){
l[rt] = L; r[rt] = R;
if(l[rt] + 1 == r[rt]){
lazy[rt] = MAXN;
return;
}
int mid = (L + R) >> 1;
build(L,mid,ls(rt)); build(mid,R,rs(rt));
}
void update(int L, int R, int x, int rt = 1){
if(l[rt]>=R or L>=r[rt]) return;
if(L<=l[rt] and r[rt]<=R){
lazy[rt] = x;
return;
}
pushdown(rt);
update(L,R,x,ls(rt)); update(L,R,x,rs(rt));
}
int query(int pos, int rt = 1){
if(l[rt] + 1 == r[rt]) return lazy[rt];
int mid = (l[rt] + r[rt]) >> 1;
pushdown(rt);
if(pos<mid) return query(pos,ls(rt));
else return query(pos,rs(rt));
}
}ST1,ST2;
struct SAM{
int len[MAXN],link[MAXN],ch[MAXN][26],tot,last,cnt[MAXN],c[MAXN],sa[MAXN],firstpos[MAXN];
SAM(){ link[0] = -1; }
void extend(int c){
int np = ++tot, p = last;
firstpos[np] = len[np] = len[p] + 1; cnt[np] = 1;
while(p!=-1 and !ch[p][c]){
ch[p][c] = np;
p = link[p];
}
if(p==-1) link[np] = 0;
else{
int q = ch[p][c];
if(len[p]+1==len[q]) link[np] = q;
else{
int clone = ++tot;
len[clone] = len[p] + 1;
link[clone] = link[q];
firstpos[clone] = firstpos[q];
memcpy(ch[clone],ch[q],sizeof(ch[q]));
link[np] = link[q] = clone;
while(p!=-1 and ch[p][c]==q){
ch[p][c] = clone;
p = link[p];
}
}
}
last = np;
}
void Radix_sort(){
for(int i = 0; i <= tot; i++) c[i] = 0;
for(int i = 0; i <= tot; i++) c[len[i]]++;
for(int i = 1; i <= tot; i++) c[i] += c[i-1];
for(int i = tot; i >= 0; i--) sa[c[len[i]]--] = i;
}
void solve(char *s){
int l = strlen(s);
for(int i = 0; i < l; i++) extend(s[i]-'a');
Radix_sort();
for(int i = tot + 1; i > 1; i--) cnt[link[sa[i]]] += cnt[sa[i]];
vector<pair<int,pair<int,int> > > vec;
for(int i = 1; i <= tot; i++) if(cnt[i]==1) vec.emplace_back(make_pair(firstpos[i],make_pair(len[link[i]]+1,len[i])));
ST1.build(1,l+1); ST2.build(1,l+1);
sort(vec.begin(),vec.end(),[](const pair<int,pair<int,int>> &lhs, const pair<int,pair<int,int>> &rhs){
return lhs.second.first > rhs.second.first;
});
for(int i = 0; i < (int)vec.size(); i++) ST1.update(vec[i].first-vec[i].second.first+1,vec[i].first+1,vec[i].second.first);
sort(vec.begin(),vec.end(),[](const pair<int,pair<int,int>> &lhs, const pair<int,pair<int,int>> &rhs){
return lhs.first > rhs.first;
});
for(int i = 0; i < (int)vec.size(); i++) ST2.update(vec[i].first-vec[i].second.second+1,vec[i].first-vec[i].second.first+1,vec[i].first);
for(int i = 1; i <= l; i++) printf("%d\n",min(ST1.query(i),ST2.query(i)-i+1));
}
}sam;
int main(){
scanf("%s",s);
sam.solve(s);
return 0;
}
BZOJ1396 识别子串【SAM+SegmentTree】的更多相关文章
- BZOJ1396:识别子串(SAM)
Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample I ...
- BZOJ-1396: 识别子串
后缀自动机+线段树 先建出\(sam\),统计一遍每个点的\(right\)集合大小\(siz\),对于\(siz=1\)的点\(x\),他所代表的子串只会出现一次,设\(y=fa[x]\),则这个点 ...
- BZOJ1396 识别子串 和 BZOJ2865 字符串识别
字符串识别 2865: 字符串识别 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 839 Solved: 261[Submit][Status][D ...
- bzoj千题计划318:bzoj1396: 识别子串(后缀自动机 + 线段树)
https://www.lydsy.com/JudgeOnline/problem.php?id=1396 后缀自动机的parent树上,如果不是叶子节点,那么至少有两个子节点 而一个状态所代表子串的 ...
- BZOJ1396: 识别子串(后缀自动机,线段树)
Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample I ...
- BZOJ1396 识别子串 字符串 SAM 线段树
原文链接http://www.cnblogs.com/zhouzhendong/p/9004467.html 题目传送门 - BZOJ1396 题意 给定一个字符串$s$,$|s|\leq 10^5$ ...
- bzoj1396识别子串(SAM+线段树)
复习SAM板子啦!考前刷水有益身心健康当然这不是板子题/水题…… 很容易发现只在i位置出现的串一定是个前缀串.那么对答案的贡献分成两部分:一部分是len[x]-fa~len[x]的这部分贡献会是r-l ...
- BZOJ bzoj1396 识别子串
题面: bzoj1396 题解: 先建出SAM,并计算right集合大小.显然符合条件的点的right集合大小为1. 对于每个right集合为1的状态显然可以算出这些状态的pos以及maxlen和mi ...
- bzoj1396: 识别子串
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...
随机推荐
- vue-vite浅析
大家好,我是小雨小雨,致力于分享有趣的.实用的文章. 内容分为原创和翻译,如果有问题,欢迎随时评论或私信,很乐意和大家一起探讨,一起进步. 分享不易,希望能够得到大家的支持和关注. vite出了好久了 ...
- Python列表推导式玩法
前言 列表做为python的基础,是必须学习的语法之一.一些基础的之前已经是反复温习和使用了,今天我们来学习它的进阶版-->列表推导式. 列表推导式: 优点:是将所有的值一次性加载到内存中,相比 ...
- (三)React Ant Design Pro + .Net5 WebApi:后端环境搭建
一. 简介 1. 平常用的core webapi 3.1,恰逢.Net5.0正式版发布了,直接开整. 2. 先学习IdentityServer4 .Autofac.EF Core,集成到后台框架里. ...
- CTF------pwn笔记
地址:http://pwnable.kr/play.php 题目: 使用MobaXterm连接(当然也可以使用别的软件进行连接,用的顺手就行) 连接成功后所以"ls"命令查看目录 ...
- Ice系列--傻瓜式服务开发IceBox
前言 相信大家在没有接触过框架之前,都自己或多或少的开发过一些应用服务.每个应用服务除了业务配置还有很多环境配置,资源配置等,这些跟部署相关的配置.服务跟配置文件是一种静态绑定的方式,更新配置还需要重 ...
- SW3518中文规格书
SW3518 是一款高集成度的多快充协议双口充电芯片, 支持 A+C 口任意口快充输出, 支持双口独立限流. 其集成了 5A 高效率同步降压变换器, 支持 PPS/PD/QC/AFC/FCP/SCP/ ...
- 镍氢可充电电池2.4V转3.3V,2V转3.3V稳压供电输出电路图
PW5100可以实现2.4V转3.3V,2V转3.3V的稳压电源电路,输出电流500MA.静态电流10uA,SOT23-5封装.输出纹波低,轻载性能高(轻载电感推荐6.8UH-10UH). PW510 ...
- Android根据pdf模板生成pdf文件
我们需要生成一些固定格式的pdf文件或者一些报表数据,那么我们可以用 iText包去做. 需要包含的jar包:iText-5.0.6.jar iTextAsian.jar ,怎样jar包导入工程 ...
- Git安装/VScode+Git+Github
Git安装/VScode+Git+Github 1. 相关简介 git 版本控制工具,支持该工具的网站有Github.BitBucket.Gitorious.国内的OS China仓库.Csdn仓库等 ...
- java.net.NoRouteToHostException: 没有到主机的路由
今天在配置Jenkins 的云服务器的时候提示:java.net.NoRouteToHostException: 没有到主机的路由,网上查到的没有主机路由问题提到的大多是防火墙问题. 查看防火墙状态: ...