题目

【题目描述】

一般地,对于一个字符串 S,和 S 中第 $ i $ 个字符 x,定义子串 $ T=S(i.j) $ 为一个关于 x 的识别子申,当且仅当:
  1.$ i \leq x \leq j $
  2.T 在 S 巾只出现一次
比如,对于 banana 的第 $ 5 $ 个字符,“nana”, “anan”,“anana”, “nan”,“banan” 和“banana”都是关于它的识别子串。
说你写一个程序,计算出对对于一个字符串 S,关于 S 的每一位的最短识别子串的长度。

【输入格式】

一行,一个由小写字母组成的字符串 S, 长度不超过 $ 10^5 $

【输出格式】

L 行,每行一个整数,第 $ i $ 行的数据表示关于 S 的第 $ i $ 个元素的最短识别子串有多长.

【样例输入】

agoodcookcooksgoodfood

【样例输出】

1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

【数据范围与提示】

对于 20% 的数据 L<=200

对于 40% 的数据 L<=5000

对于 60% 的数据 L<=20000

对于 100% 的数据 L<=100000

题解

可以发现,对于每一个唯一识别的子串,都可以更新一段答案

可以发现,在后缀自动机上的每一个叶子节点 $ x $,都是一段唯一识别的子串

考虑更新,记 $ l=len(x)-len(fa(x)),r=len(x) $,那么对于 $ [1,l-1] $ 的贡献为 $ r-i+1 $,对于 $ [l,r] $ 的贡献为 $ r-l+1 $

可以把 $r-i+1$ 的 $ i $ 提出,转化为 $query(i)-i$,开两棵线段树分别统计即可

代码

 #include<bits/stdc++.h>
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
int R(){
int x;bool f=;char ch;_(!)if(ch=='-')f=;x=ch^;
_()x=(x<<)+(x<<)+(ch^);return f?x:-x;}
const int N=1e6+;
int n,las=,cnt=,Rt=;
char s[N];bool f[N];
struct node{int ch[],fa,len;}tr[N];
void extend(int c){
int p=las,np=las=++cnt,q,nq;
tr[np].len=tr[p].len+;
while(!tr[p].ch[c]&&p)
tr[p].ch[c]=np,p=tr[p].fa;
if(!p)return void(tr[np].fa=Rt);
if(tr[p].len+==tr[q=tr[p].ch[c]].len)return void(tr[np].fa=q);
tr[nq=++cnt].len=tr[p].len+;
memcpy(tr[nq].ch,tr[q].ch,sizeof tr[q].ch);
tr[nq].fa=tr[q].fa,tr[np].fa=tr[q].fa=nq;
while(p&&tr[p].ch[c]==q)
tr[p].ch[c]=nq,p=tr[p].fa;
return;
}
class seg{
private:
#define Ls rt<<1
#define Rs rt<<1|1
public:
int tr[N];
seg(){memset(tr,0x3f,sizeof tr);}
void update(int rt,int l,int r,int ql,int qr,int v){
if(ql>qr)return;
if(ql<=l&&qr>=r)return void(tr[rt]=min(tr[rt],v));
int mid=l+r>>;
if(ql<=mid)update(Ls,l,mid,ql,qr,v);
if(qr>mid)update(Rs,mid+,r,ql,qr,v);
return;
}
int query(int rt,int l,int r,int k){
if(l==r)return tr[rt];
int mid=l+r>>;
if(k<=mid)return min(tr[rt],query(Ls,l,mid,k));
else return min(tr[rt],query(Rs,mid+,r,k));
}
}T1,T2;
int main(){
scanf("%s",s+),n=strlen(s+);
for(int i=;i<=n;i++)extend(s[i]-'a');
for(int i=;i<=cnt;i++)f[tr[i].fa]=;
for(int i=;i<=cnt;i++)
if(!f[i]){
int x=tr[i].len-tr[tr[i].fa].len,y=tr[i].len;
T1.update(,,n,,x-,y+),T2.update(,,n,x,y,tr[tr[i].fa].len+);
}
for(int i=;i<=n;i++)
printf("%d\n",min(T1.query(,,n,i)-i,T2.query(,,n,i)));
return ;
}

识别子串 (string)——后缀自动机+线段树的更多相关文章

  1. bzoj千题计划318:bzoj1396: 识别子串(后缀自动机 + 线段树)

    https://www.lydsy.com/JudgeOnline/problem.php?id=1396 后缀自动机的parent树上,如果不是叶子节点,那么至少有两个子节点 而一个状态所代表子串的 ...

  2. BZOJ1396&2865 识别子串 【后缀自动机 + 线段树】

    题目 输入格式 一行,一个由小写字母组成的字符串S,长度不超过10^5 输出格式 L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. 输入样例 agoodcookcooksg ...

  3. BZOJ1396: 识别子串(后缀自动机 线段树)

    题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...

  4. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  5. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

  6. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  7. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  8. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  9. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

随机推荐

  1. clone对象的克隆

    用一句简单的话来说就是浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针. 等多 http:/ ...

  2. C语言在32位和64位机器下数字数据类型的字节数取决于编译器和平台, 主要由编译器决定。

    C语言中数字数据类型的字节数 C声明 32位机器 64位机器 char 1 1 short int 2 2 int   4 4 //整型在32位和64位下都是4个字节 long int   4 8 l ...

  3. HeapCreate深入研究

    本机:win7(x86),4G内存 #include"stdafx.h"#include<windows.h>#include<stdio.h>#inclu ...

  4. 12-01Js对象##正则表达式##

    正则表达式:是一个字符一个字符的验证,通过量词验证字符串: 1.什么是RegExp?RegExp是正则表达式的缩写.RegExp 对象用于规定在文本中检索的内容. 2.定义RegExp:var +变量 ...

  5. App启动原理和启动过程

        一.程序启动原理 1.1.main函数中执行了一个UIApplicationMain这个函数UIApplicationMain(int argc, char *argv[], NSString ...

  6. Tiny4412 Linux 内核启动流程

    Linux内核的启动分为压缩内核和非压缩内核两种,这里我们以压缩内核为例.压缩内核运行时,将运行一段解压缩程序,得到真正的内核镜像,然后跳转到内核镜像运行.此时,Linux进入非压缩内核入口,在非压缩 ...

  7. zookeeper 安装配置注意事项

    zoo.cfg 1.server.1/2/3  有几台配置几个 ​2.配置好hosts映射之后可以用node1替代IP地址 3.dataLogDir  下面配置的logs 的目录一定要创建 4.dat ...

  8. 0016_练习题d2

    __author__ = 'qq593' #!/usr/bin/env python #-*- coding:utf-8 -*- #元素分类,有如下值集合[11,22,33,44,55,66,77,8 ...

  9. python爬虫(4)--Cookie的使用

    Cookie,指某些网站为了辨别用户身份.进行session跟踪而储存在用户本地终端上的数据(通常经过加密) 比如说有些网站需要登录后才能访问某个页面,在登录之前,你想抓取某个页面内容是不允许的.那么 ...

  10. hibernate学习笔记(1)基础配置与jar包

    下载hibernate基础jar包,并解压hibernate-core-4.2.4.final 在myeclipse中添加hibernate的dtd支持: location为D:\学习\imooc-h ...