后缀自动机+线段树

先建出\(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. SET ANSI_NULLS ON 在T-SQL中是什么意思

    from:https://www.cnblogs.com/kekong/p/6731321.html Transact-SQL 支持在与空值进行比较时,允许比较运算符返回 TRUE 或 FALSE. ...

  2. Android经常使用工具类DateUtils(二)

    在编写代码中,会经经常使用到时间Date这个类,小编整理了一些经常使用的时间工具类.供大家參考. import java.text.ParseException; import java.text.S ...

  3. python MD5操作

    def my_md5(str): import hashlib new_str = str.encode() #把字符串转成bytes类型 # new_str = b'%s'%str #把字符串转成b ...

  4. 事务处理笔记《二》.Net框架下的事务处理技术

    术语:DTC(分布式事务协调器) .Net中实现事务的几种方法总结:一. 通过后台数据库的SQL语句实现事务,在台数据库端通过语句来实现转帐业务,如下: Set xact_abort on Begin ...

  5. Python抓取网页并保存为PDF

    https://blog.csdn.net/shenwanjiang111/article/details/67634794

  6. SSL证书的生成

    openssl工具下载路径:链接:https://pan.baidu.com/s/1o0-s8OplHZt55Cio2HmjVA 密码:u759 1.使用openssl工具生成一个RSA秘钥      ...

  7. jQuery插件——1.编写规则

    jQuery插件编写规则如下: 1.命名规则:jquery.[插件名称].js 2.所有对象方法都应当附加到jQuery.fn对象上:所有的全局方法都应当附加到jQuery对象上. 3.在插件内部,t ...

  8. python基础里的那些为什么?

    一.执行python脚本的两种方式? 直接在解释器里编写并在解释器里执行 文件编写,并在终端通过 python 路径  这种方式执行 好,我们就以输出hello world这个例子来比较两种方式的不同 ...

  9. SQLServer中行列转换Pivot UnPivot

    PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PIVOT(聚合函数(列) FOR 列 in (…) )AS P ...

  10. bzoj3007 解救小云公主

    3007: 解救小云公主 Time Limit: 5 Sec  Memory Limit: 512 MB Submit: 159  Solved: 71 [id=3007" style=&q ...