BZOJ 1396 识别子串 (后缀自动机+线段树)
题目大意:
给你一个字符串S,求关于每个位置x的识别串T的最短长度,T必须满足覆盖x,且T在S中仅出现一次
神题
以节点x为结尾的识别串,必须满足它在$parent$树的子树中只有一个$endpos$节点
若令$fa=pre_{x},L=dep_{fa},R=dep_{x}$
1.对于以$i\in[1,R-L]$为开头,以$R$为结尾的串,合法串的长度是$R-i+1$
如果在$S_{1...x}$串中不断删去开头的字符,删到串剩余长度为$dep_{fa}$时结束
上述过程在$parent$树里的表现为,从表示串$S_{1...x}$的节点$a$,删掉最后一个字符后,跳到了表示$S_{x-dep_{fa}+1...x}$的节点$b$
不必考虑$b$的$right$集合大小,因为$R$是递增的,如果$b$节点的串能作为识别串,$a$节点的答案一定不如$b$优秀
所以我们干脆只讨论不跳到$b$的情况就行了
那么$i\in[1,R-L]$为开头,$R$为结尾的的串一定都能作为$[1,R-L]$的识别串
2.对于以$i\in[R-L+1,R]$为开头,以$R$为结尾的串,合法串的长度是$R-L+1$
一样的道理,如果从$a$跳到了$b$,$b$节点的串可能不合法,但$a$节点的串一定合法
那么以$L$为开头,$R$为结尾的的串一定都能作为$[1,R-L+1]$的识别串
区间修改,单点查询,开两颗线段树维护一下就好
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 105000
#define S1 (N1<<1)
#define T1 (N1<<2)
#define ll long long
#define uint unsigned int
#define rint register int
#define il inline
#define inf 0x3f3f3f3f
#define idx(X) (X-'a')
using namespace std; int gint()
{
int ret=,fh=;char c=getchar();
while(c<''||c>''){if(c=='-')fh=-;c=getchar();}
while(c>=''&&c<=''){ret=ret*+c-'';c=getchar();}
return ret*fh;
}
char str[N1];
int len;
struct Seg{
int mi[S1<<];
void pushdown(int rt){
mi[rt<<]=min(mi[rt<<],mi[rt]);
mi[rt<<|]=min(mi[rt<<|],mi[rt]);}
void build(int l,int r,int rt)
{
mi[rt]=inf;
if(l==r) return;
int mid=(l+r)>>;
build(l,mid,rt<<);
build(mid+,r,rt<<|);
}
void update(int L,int R,int l,int r,int rt,int w)
{
if(L<=l&&r<=R) {mi[rt]=min(w,mi[rt]);return;}
pushdown(rt);int mid=(l+r)>>;
if(L<=mid) update(L,R,l,mid,rt<<,w);
if(R>mid) update(L,R,mid+,r,rt<<|,w);
}
int query(int x,int l,int r,int rt)
{
if(l==r) return mi[rt];
pushdown(rt);int mid=(l+r)>>;
if(x<=mid) return query(x,l,mid,rt<<);
else return query(x,mid+,r,rt<<|);
}
}s1,s2;
namespace SAM{
int trs[S1][],pre[S1],dep[S1],ed[S1],sz[S1],tot,la;
void init(){tot=la=;}
void reduct(){la=;}
void insert(int c)
{
int p=la,np=++tot,q,nq;la=np;
dep[np]=dep[p]+;ed[np]=;
for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
if(!p) {pre[np]=;return;}
q=trs[p][c];
if(dep[q]==dep[p]+) pre[np]=q;
else{
pre[nq=++tot]=pre[q];
pre[q]=pre[np]=nq;
dep[nq]=dep[p]+;
memcpy(trs[nq],trs[q],sizeof(trs[q]));
for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
}
}
int hs[S1],que[S1],ans[N1];
void solve()
{
for(int i=;i<=tot;i++) hs[dep[i]]++;
for(int i=;i<=len;i++) hs[i]+=hs[i-];
for(int i=;i<=tot;i++) que[hs[dep[i]]--]=i;
int x,fx;
for(int i=tot-;i>;i--)
{
x=que[i];
sz[x]+=(ed[x]?:);
sz[pre[x]]+=sz[x];
}
s1.build(,tot,);
s2.build(,tot,);
for(int i=;i<=tot;i++)
{
x=que[i],fx=pre[x];
if(sz[x]>) continue;
if(dep[fx]>=) s1.update(dep[x]-dep[fx]+,dep[x],,tot,,dep[fx]+);
if(dep[fx]+<=dep[x]) s2.update(,dep[x]-dep[fx],,tot,,dep[x]);
}
for(int i=;i<=len;i++)
{
ans[i]=min(s1.query(i,,tot,),s2.query(i,,tot,)-i+);
printf("%d\n",ans[i]);
}
}
}; int main()
{
//freopen("t2.in","r",stdin);
scanf("%s",str+);
len=strlen(str+);
SAM::init();
for(int i=;i<=len;i++)
SAM::insert(idx(str[i]));
SAM::solve();
return ;
}
BZOJ 1396 识别子串 (后缀自动机+线段树)的更多相关文章
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...
- BZOJ1396: 识别子串(后缀自动机 线段树)
题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...
- BZOJ 1396&&2865 识别子串[后缀自动机 线段树]
Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如, ...
- BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)
题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...
- BZOJ 1396 识别子串 (后缀自动机、线段树)
手动博客搬家: 本文发表于20181221 00:58:26, 原地址https://blog.csdn.net/suncongbo/article/details/85150962 嗯,以后博客内容 ...
- 【BZOJ1396】识别子串 - 后缀自动机+线段树
题意: Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. 题解: ...
- bzoj1396&&2865 识别子串 后缀自动机+线段树
Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample Input agoodco ...
- bzoj 1396: 识别子串【SAM+线段树】
建个SAM,符合要求的串显然是|right|==1的节点多代表的串,设si[i]为right集合大小,p[i]为right最大的r点,这些都可以建出SAM后再parent树上求得 然后对弈si[i]= ...
- bzoj 1396/2865: 识别子串 后缀自动机+线段树
水水的字符串题 ~ #include <map> #include <cstdio> #include <cstring> #include <algorit ...
随机推荐
- POJ Pseudoprime numbers( Miller-Rabin素数测试 )
链接:传送门 题意:题目给出费马小定理:Fermat's theorem states that for any prime number p and for any integer a > 1 ...
- Windows下PHP服务nginx不能使用file_get_contents的原因
注意:本文为转载,原文链接:Windows下PHP服务nginx不能使用file_get_contents/curl/fopen的原因! 一.问题说明 在Windows环境下搭建了一个本地开发服务环境 ...
- C#使用 ComboBox 控件
Combox控件是一个下拉选择的控件,再做上位机的时候会经常用到,这里记录一下我是怎么用. 1.拉出一个combox控件 2.控件属性选为不可编辑,可编辑的话,你选择下拉框的内容后可以改下拉框里的内容 ...
- ContextLoaderListener的说明
ContextLoaderListener是配置在web.xml里的,具体如下: <!-- ContextLoaderListener是个监听器,用来监听容器启动事件,监听到容器启动事件后 其c ...
- MyBatis中动态SQL元素的使用
掌握MyBatis中动态SQL元素的使用 if choose(when,otherwise) trim where set foreach <SQL>和<include> 在应 ...
- hadoop的理解
知乎上有个人写的感觉挺好理解的,不知道对不对,先截图存着
- 机房-动环-江森ODS系统
优势: 标准的BACnet系统平台 开放的集成特性 支持Desktop and Server平台 支持多达100个NxE 支持无线应用,可以手机访问 DCIM---数据中心基础架构管理平台介绍 不同于 ...
- PHP别名引用错误:“The use statement with non-compound name … has no effect”
别名概述 PHP5.3+支持命名空间:namespace,命名空间的一个重要功能是能够使用别名(alias)来引用一个符合规则的名字. 命名空间支持3中形式的别名引用(或称之为引入)方式:类(clas ...
- MFC 与Excel文件的交互操作
假日快要结束了.带着沉重的心情写下之前关于MFC与Excel文件交互的总结. 因为VS的版本号不同可能在操作上有些差异.所以在此指明下本篇文章的project环境为VS2013,也建议大家用最新的. ...
- Android自己定义View之组合控件 ---- LED数字时钟
先上图 LEDView效果如图所看到的. 之前看到一篇博客使用两个TextView实现了该效果.于是我想用自己定义控件的方式实现一个LEDView.使用时就可以直接使用该控件. 採用组合控件的方式,将 ...