很容易想到只考虑后缀长度必须为\(max(height[rk[i]],height[rk[i]+1])+1\)(即\([i,i+x-1]\)代表的串只出现过一次)然后我正着做一遍反着做一遍,再取一个\(min\)最后挂了。。。

设\(x=max(height[rk[i]],height[rk[i]+1])+1\)我们考虑\(i\)的贡献,会给区间\([i,i+x-1]\)一个贡献x

,设\(r=i+x-1\)然后会给r+1一个贡献x+1就是(r+1)-i+1,接着是r+2的贡献(r+2)-i+1。。。

最后我们对每一个点求出这个点的最小的贡献。这堆东西可以用线段树维护。

值得注意的一点是当i+x-1>n时并不能产生贡献,因为此时已经到了字符串末尾。

我们无法加上\(max(height[rk[i]],height[rk[i]+1])+1\)最后的那个1。

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
#define mid ((l+r)>>1)
#define ls now<<1
#define rs now<<1|1
const int N=501000;
int c[N],x[N],y[N],sa[N],rk[N],height[N],n,m;
int lazy1[N*5],lazy2[N*5],mn[N*5];
char s[N];
void get_sa(){
for(int i=1;i<=n;i++)c[x[i]=s[i]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;
for(int i=n-k+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[x[i]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
for(int i=1;i<=n;i++)swap(x[i],y[i]);
x[sa[1]]=1;num=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])?num:++num;
if(n==num)break;
m=num;
}
}
void get_height(){
int k=0;
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1;i<=n;i++){
if(rk[i]==1)continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
height[rk[i]]=k;
}
}
void build(int l,int r,int now){
if(l==r){mn[now]=n;return;}
build(l,mid,ls);
build(mid+1,r,rs);
}
void update(int now){
mn[now]=min(mn[ls],mn[rs]);
}
void pushdown(int l,int r,int now){
if(l==r)return;
if(lazy1[now]){
mn[ls]=min(mn[ls],lazy1[now]);
mn[rs]=min(mn[rs],lazy1[now]);
if(lazy1[rs])lazy1[rs]=min(lazy1[rs],lazy1[now]);
else lazy1[rs]=lazy1[now];
if(lazy1[ls])lazy1[ls]=min(lazy1[ls],lazy1[now]);
else lazy1[ls]=lazy1[now];
lazy1[now]=0;
}
if(lazy2[now]){
mn[ls]=min(mn[ls],l+lazy2[now]);
mn[rs]=min(mn[rs],mid+1+lazy2[now]);
if(lazy2[rs])lazy2[rs]=min(lazy2[rs],lazy2[now]);
else lazy2[rs]=lazy2[now];
if(lazy2[ls])lazy2[ls]=min(lazy2[ls],lazy2[now]);
else lazy2[ls]=lazy2[now];
lazy2[now]=0;
}
}
void add1(int l,int r,int L,int R,int w,int now){
pushdown(l,r,now);
if(l==L&&r==R){
lazy1[now]=w;
mn[now]=min(mn[now],w);
return;
}
if(L>mid)add1(mid+1,r,L,R,w,rs);
else if(R<=mid)add1(l,mid,L,R,w,ls);
else add1(l,mid,L,mid,w,ls),add1(mid+1,r,mid+1,R,w,rs);
update(now);
}
void add2(int l,int r,int L,int R,int w,int now){
if(L>R)return;
pushdown(l,r,now);
if(l==L&&r==R){
lazy2[now]=w;
mn[now]=min(l+w,mn[now]);
return;
}
if(L>mid)add2(mid+1,r,L,R,w,rs);
else if(R<=mid)add2(l,mid,L,R,w,ls);
else add2(l,mid,L,mid,w,ls),add2(mid+1,r,mid+1,R,w,rs);
update(now);
}
int check(int l,int r,int x,int now){
pushdown(l,r,now);
if(l==r)return mn[now];
if(x>mid)return check(mid+1,r,x,rs);
else return check(l,mid,x,ls);
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
m=122;
get_sa();get_height();
build(1,n,1);
for(int i=1;i<=n;i++){
int tmp=max(height[rk[i]],height[rk[i]+1])+1;
if(i+tmp-1<=n)add1(1,n,i,i+tmp-1,tmp,1);
add2(1,n,i+tmp,n,-i+1,1);
}
for(int i=1;i<=n;i++)printf("%d\n",check(1,n,i,1));
return 0;
}

BZOJ 2865 字符串识别(后缀数组+线段树)的更多相关文章

  1. BZOJ 2865 字符串识别 | 后缀数组 线段树

    集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...

  2. bzoj 2865 字符串识别 —— 后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2865 唯一出现的子串就是每个后缀除去和别的后缀最长的 LCP 之外的前缀: 所以用这个更新一 ...

  3. bzoj 2865 字符串识别——后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2865 做出 ht[ ] 之后,sa[ ] 上每个位置和它前面与后面取 LCP ,其中较大的长 ...

  4. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

  5. bzoj 1396: 识别子串 && bzoj 2865: 字符串识别【后缀数组+线段树】

    根据height数组的定义,和当前后缀串i最长的相同串的长度就是max(height[i],height[i+1]),这个后缀贡献的最短不同串长度就是len=max(height[i],height[ ...

  6. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  7. BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)

    题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...

  8. BZOJ 5496: [2019省队联测]字符串问题 (后缀数组+主席树优化建图+拓扑排序)

    题意 略 分析 考场上写了暴力建图40分溜了-(结果只得了30分) 然后只要优化建边就行了 首先给出的支配关系无法优化,就直接A向它支配的B连边. 考虑B向以B作为前缀的所有A连边,做一遍后缀数组,两 ...

  9. 【XSY1551】往事 广义后缀数组 线段树合并

    题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...

随机推荐

  1. 2015 Objective-C 新特性

    Overview 自 WWDC 2015 推出和开源 Swift 2.0 后,大家对 Swift 的热情又一次高涨起来,在羡慕创业公司的朋友们大谈 Swift 新特性的同时,也有很多像我一样工作上依然 ...

  2. Unity 动画系统(Mecanim) 术语及翻译 表格

    原文 翻译 Animation Clip 视频片段 Avatar 阿凡达 Retargeting 重定向 Rigging 绑定 skinning 蒙皮 Animator Component 动画组件 ...

  3. rem 自适应布局 bootstrap 移动端适配

    移动端适配用:rem 自使用布局用:bootstrap

  4. Spark作业执行

    Spark中一个action触发一个job的执行,在job提交过程中主要涉及Driver和Executor两个节点. Driver主要解决 1. RDD 依赖性分析,生成DAG. 2. 根据RDD D ...

  5. [HDU1160]FatMouse's Speed

    题目大意:读入一些数(每行读入$w[i],s[i]$为一组数),要求找到一个最长的序列,使得符合$w[m[1]] < w[m[2]] < ... < w[m[n]]$且$s[m[1] ...

  6. javascript 中一些奇葩的日期换算

    1.获取今天的0时0分0秒(常用于开始日期的获取) new Date(new Date().toLocaleDateString()); // Mon Nov 12 2018 00:00:00 GMT ...

  7. 关于samsung连接BLE设备的一些资料汇总和开发过程一些经验总结

    1 忙了这么久,终于有时间把最近几个月弄的东西整理一下,顺便我的开发过程和经历. 被公司分到做一个蓝牙4.0的项目,对这种软硬结合的东西也比较感兴趣,所以很快投入到android蓝牙4.0的项目中来. ...

  8. 洛谷P5238 整数校验器

    看到没有边读入边处理的,我来水一发 我们要看一下有那些情况是格式不合法的 单独的负号 -0(后面可以有其他数字) 0 +(后面一些数字) 我们用快速读入的方法 读取字符进行处理 还有可能超出范围的 考 ...

  9. DCL授权命令

    create user 用户名//创建用户    grant DBA to 用户名//授权    revoke //撤销权限

  10. Winserver服务器-AD字段对照简图

    AD字段对照简图