后缀自动机+线段树

先建出\(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. pycharn设置git提交代码

    1.设置pycharm的git地址 2.设置git地址及本地路径 3.提交代码

  2. 40、DrawerLayout使用详情

    1.主内容视图一定要是DrawerLayout的第一个子视图 2.主内容视图宽度和高度匹配父视图,即“match_parent” 3.必须显示指定抽屉视图(如ListView)的 android:la ...

  3. java MD5工具类

    package com.common.tools; import java.security.MessageDigest; /** * MD5加密工具类 * <功能详细描述> * * @a ...

  4. JS实现全选,全不选

    <script type="text/javascript"> function selectItem() { document.getElementById(&quo ...

  5. python系列六:Python3元组tuple

    '''元组与列表类似,不同之处在于元组的元素不能修改.元组使用小括号,列表使用方括号.''''''uple元素不可变有一种特殊情况,当元素是可变对象时.对象内部属性是可以修改的!tuple的不可变限制 ...

  6. etl接口测试总结

    刚做完一个项目接触到了etl接口,趁还热乎做个总结. etl接口功能测试点总结:1.数据量的检查:目标表与源表数据量是否一致2.字段正确性:拉取源表字段是否为目标表所需要字段(会出现拉错字段情况)3. ...

  7. 异动K线2--600532做一个分析时再给大家一只个股和近日大盘的分析

    http://bbs.tianya.cn/post-stocks-612892-3.shtml ————看了一页就感觉没什么太大的意义 选时重于选股 这是一条股市生存的基本法则 看看天涯真正的高手 现 ...

  8. 【转】python面向对象中的元类

    type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的. 比方说我们要定义一个Hello的class,就写一个hello.py模块: class Hel ...

  9. MapInfo 文件格式说明

    MapInfo 文件格式说明(id.map.tab.dat) (1). 属性数据的表结构文件.TAB 属性数据表结构文件定义了地图属性数据的表结构,包括字段数.字段名称.字段类型和字段宽度.索引字段及 ...

  10. php备份mysql数据库

    <?php /*程序功能:mysql数据库备份功能*/ ini_set('max_execution_time','0'); ini_set('memory_limit','1024M');// ...