后缀自动机+线段树

先建出\(sam\),统计一遍每个点的\(right\)集合大小\(siz\),对于\(siz=1\)的点\(x\),他所代表的子串只会出现一次,设\(y=fa[x]\),则这个点代表的子串即为\((1...len[x]-len[y],len[x])\),对于子串\((len[x]-len[y],len[x])\)的每一个点,这个子串都是他的识别子串,长度固定\(len[y]+1\),而对于一个点\(i\in[1,len[x]-len[y]-1]\),子串\((i,len[x])\)一定是他的一个识别子串,长度\(len[x]+1-i\)。可以用两棵线段树分别存\(len[y]+1\)与\(len[x]+1\),单点查询最小值得到答案

#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
struct xdt{
int poi[maxn<<2],lazy[maxn<<2];
void updata(int x){
poi[x]=min(poi[x<<1],poi[x<<1|1]);
}
void pushdown(int x){
if(!lazy[x]) return;
poi[x<<1]=min(poi[x<<1],lazy[x]);
lazy[x<<1]=lazy[x<<1]?min(lazy[x<<1],lazy[x]):lazy[x];
poi[x<<1|1]=min(poi[x<<1|1],lazy[x]);
lazy[x<<1|1]=lazy[x<<1|1]?min(lazy[x<<1|1],lazy[x]):lazy[x];
lazy[x]=0;
}
void build(int l,int r,int now){
poi[now]=0x7fffffff;
if(l==r) return;
int mid=l+r>>1;
build(l,mid,now<<1);
build(mid+1,r,now<<1|1);
}
void revise(int lc,int rc,int l,int r,int now,int z){
if(lc==l&&rc==r){
poi[now]=min(poi[now],z);
if(lazy[now]==0) lazy[now]=z;
else lazy[now]=min(lazy[now],z);
return;
}
pushdown(now);
int mid=lc+rc>>1;
if(l<=mid) revise(lc,mid,l,min(mid,r),now<<1,z);
if(r>mid) revise(mid+1,rc,max(mid+1,l),r,now<<1|1,z);
updata(now);
}
int query(int lc,int rc,int x,int now){
if(lc==rc) return poi[now];
pushdown(now);
int mid=lc+rc>>1;
if(x<=mid) return query(lc,mid,x,now<<1);
else return query(mid+1,rc,x,now<<1|1);
}
}t1,t2;
struct SAM{
int son[maxn][26],len[maxn],siz[maxn],fa[maxn],tax[maxn],a[maxn];
int tot,last,n;
char s[maxn];
void insert(int x){
int p=last,np=++tot;
len[np]=len[p]+1;
siz[np]=1;
while(~p&&!son[p][x])
son[p][x]=np,p=fa[p];
if(p==-1)
fa[np]=0;
else{
int q=son[p][x];
if(len[q]==len[p]+1)
fa[np]=q;
else{
int nq=++tot;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q];
len[nq]=len[p]+1;
fa[q]=fa[np]=nq;
while(~p&&son[p][x]==q)
son[p][x]=nq,p=fa[p];
}
}
last=np;
}
void Qsort(){
for(int i=0;i<=n;i++) tax[i]=0;
for(int i=1;i<=tot;i++) tax[len[i]]++;
for(int i=1;i<=n;i++) tax[i]+=tax[i-1];
for(int i=1;i<=tot;i++) a[tax[len[i]]--]=i;
}
void ycl(){
last=tot=0,fa[0]=-1;
for(int i=1;i<=n;i++) insert(s[i]-'a');
Qsort();
for(int i=tot;i>=1;i--) siz[fa[a[i]]]+=siz[a[i]];
t1.build(1,n,1),t2.build(1,n,1);
for(int i=1;i<=tot;i++)
if(siz[i]==1){
int l=len[i]-len[fa[i]],r=len[i];
t1.revise(1,n,l,r,1,r-l+1);
// printf("->%d\n",t1.query(1,n,2,1));
if(l!=1) t2.revise(1,n,1,l-1,1,r+1);
}
}
void query(){
// printf("->%d\n",t1.query(1,n,2,1));
for(int i=1;i<=n;i++)
printf("%d\n",min(t1.query(1,n,i,1),t2.query(1,n,i,1)-i));
}
}sam;
int main(){
// freopen(".in","r",stdin);
scanf("%s",sam.s+1),sam.n=strlen(sam.s+1);
sam.ycl();
sam.query();
return 0;
}


BZOJ-1396: 识别子串的更多相关文章

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

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

  2. BZOJ 1396:识别子串 SA+树状数组+单调队列

    1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 381  Solved: 243[Submit][Status][Discuss] ...

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

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

  4. bzoj 1396 识别子串 后缀树+线段树

    题目大意 给定一个长度\(\le100000\)的字符串 求每一个位置的最短识别子串 对于位置\(x\),能识别子串\(s[i...j]\)的条件是 1.\(i\le x \le j\) 2.\(s[ ...

  5. BZOJ 1396 识别子串 (后缀自动机、线段树)

    手动博客搬家: 本文发表于20181221 00:58:26, 原地址https://blog.csdn.net/suncongbo/article/details/85150962 嗯,以后博客内容 ...

  6. ●BZOJ 1396 识别子串

    题链: http://www.joyoi.cn/problem/tyvj-2301(非权限OI患者,苟且在joyoi...)题解: 后缀自动机,线段树 先对原串建立后缀自动机,不难发现, 会影响答案是 ...

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

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

  8. BZOJ 1396 识别子串 (后缀自动机+线段树)

    题目大意: 给你一个字符串S,求关于每个位置x的识别串T的最短长度,T必须满足覆盖x,且T在S中仅出现一次 神题 以节点x为结尾的识别串,必须满足它在$parent$树的子树中只有一个$endpos$ ...

  9. bzoj 1396: 识别子串【SAM+线段树】

    建个SAM,符合要求的串显然是|right|==1的节点多代表的串,设si[i]为right集合大小,p[i]为right最大的r点,这些都可以建出SAM后再parent树上求得 然后对弈si[i]= ...

  10. BZOJ bzoj1396 识别子串

    题面: bzoj1396 题解: 先建出SAM,并计算right集合大小.显然符合条件的点的right集合大小为1. 对于每个right集合为1的状态显然可以算出这些状态的pos以及maxlen和mi ...

随机推荐

  1. Linux下IP等网络配置

    Linux下IP等网络配置: 我所知道一共三种方式,下面简单介绍(注意:网络配置必须”root管理员“登录才能进行配置). 一 1.首先在命令行输入[ifconfig]命令,可看到相关网络信息,其中” ...

  2. eclipse中如何查看一个android模拟器的内部文件

    eclipse中如何查看一个android模拟器的内部文件,有时要在其中添加一个文件夹或是什么的,要手动的做这件事,而不能够用代码去完成时,就要用这个方法了. 1.首先,打开一个安卓模拟器. 2.这个 ...

  3. 41、Android中当数据库需要更新时我们该怎么办?

    转载  http://blog.csdn.net/jiangwei0910410003/article/details/39670813

  4. Django项目部署(django+guncorn+virtualenv+nginx)

    一.说明 为了django项目部署到生产环境上,能够稳定的运行,且能够同时指出http和https的访问,对django的部署进行了一些研究,决定采用django + gunicorn + virtu ...

  5. EasyPlayer.js网页全终端播放器安装使用文档

    EasyPlayer.js 集 rtmp, hls, flv, websocket 于一身的网页直播/点播播放器, 使用简单, 功能强大 属性(Property) video-url 视频流地址 St ...

  6. locust参数化

    前面用篇专门讲了requests实现接口的参数关联案例,这里直接转化成locust脚本就行了 # coding:utf-8 from locust import HttpLocust, TaskSet ...

  7. PyQt4测试安装ok的程序

    # -*- coding: utf-8 -*- """ ------------------------------------------------- File Na ...

  8. 电信、网通、联通等恶意DNS劫持跳广告页面的解决方法

    中国电信.网通.联通ADSL用户必读:中国电信.网通.联通劫持dns(中国电信.网通.联通劫持ie浏览器)解决方案D... 宽带连接有 也能上网但是本地连接一直显示为受限制的解决方法 我的电脑一直显示 ...

  9. Android:日常学习笔记(8)———探究UI开发(3)

    Android:日常学习笔记(8)———探究UI开发(3) 详解四种基本布局 前言 布局定义用户界面的视觉结构,如Activity或应用小部件的 UI.您可以通过两种方式声明布局: 在 XML 中声明 ...

  10. 流量分析系统---kafka集群部署

    1.集群部署的基本流程 Storm上游数据源之Kakfa 下载安装包.解压安装包.修改配置文件.分发安装包.启动集群 2.基础环境准备 安装前的准备工作(zk集群已经部署完毕)  关闭防火墙 chk ...