后缀自动机+线段树

先建出\(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. WPF简介:VS创建桌面应用程序

    1.简介 1/ 什么是WPF WPF,Windows Presentation Foundation也,译过来就是"Windows呈现基础",你看它的目的非常明确,就是用来把数据& ...

  2. hdu1754(splay tree 单点更新,成段查询)

    题意就是简单的点更新,成段查询. splay tree 果真是常数比较大的log(n)操作. 比线段树还慢了这么多. // // main.cpp // splay // // Created by ...

  3. Balala Power!(大数+思维)

    Balala Power! Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)T ...

  4. API网关性能比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd API 网关出现的原因

    API网关性能比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd http://www.infoq.com/cn/articles/compa ...

  5. SQL 排序的N种方法

    一.手工查询得出名次 select * ,select count(*)+1 from T2 where T2.[成绩] > T1.[成绩] as 名次 from T1 结果:  1 2 3 3 ...

  6. Python 获取文件路径及文件目录

    import os print (os.path.dirname(__file__)) print (os.path.abspath(__file__)) print (os.path.abspath ...

  7. [二次开发]dede文章页面怎样显示作者的头像

    dede在文章页面显示作者仅仅是显示其username,可是假如我想把dede改造成较为社交化的站点.我认为是有必要显示作者的头像的,可是官方并没有相应的模版标签. 在网上看到解决问题的办法基本上是直 ...

  8. BDC程序步骤

    (1)记录屏幕操作: (2)产生相关程序和数据格式文件: (3)调整数据文件: (4)运行BDC产生的程序读取文件导入数据: (5)源代码分析: (6)用BDC 导入单据: 在理解ABAP 开发的sc ...

  9. JDBC注册驱动程序的三种方式

    1. Class.forName("com.mysql.jdbc.Driver");//加载数据库驱动 Class.forName("com.mysql.jdbc.Dri ...

  10. 1.4 使用电脑测试MC20的接收英文短信功能

    需要准备的硬件 MC20开发板 1个 https://item.taobao.com/item.htm?id=562661881042 GSM/GPRS天线 1根 https://item.taoba ...