题目

【题目描述】

一般地,对于一个字符串 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. Servlet3.0之八:基于Servlet3.0的文件上传@MultipartConfig

    在Servlet2.5中,我们要实现文件上传功能时,一般都需要借助第三方开源组件,例如Apache的commons-fileupload组件,在Servlet3.0中提供了对文件上传的原生支持,我们不 ...

  2. 愿天下有情人都是失散多年的兄妹(bfs)

    L2-016. 愿天下有情人都是失散多年的兄妹 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 呵呵.大家都知道五服以内不得通婚 ...

  3. HTTP-Runoob:HTTP请求方法

    ylbtech-HTTP-Runoob:HTTP请求方法 1.返回顶部 1. HTTP请求方法 根据HTTP标准,HTTP请求可以使用多种请求方法. HTTP1.0定义了三种请求方法: GET, PO ...

  4. webapi中使用token验证(JWT验证)

    本文介绍如何在webapi中使用JWT验证 准备 安装JWT安装包 System.IdentityModel.Tokens.Jwt 你的前端api登录请求的方法,参考 axios.get(" ...

  5. 2015.1.8 Left join 左连接

    格式 select f1.a, f2.b form f1 left jion f2 on .... 注意:左边的查询部分只能有select和from,不能出现where order by等.若有必须在 ...

  6. 问题:oracle 字符串转换成日期;结果:[oracle] to_date() 与 to_char() 日期和字符串转换

    to_date("要转换的字符串","转换的格式")   两个参数的格式必须匹配,否则会报错. 即按照第二个参数的格式解释第一个参数. to_char(日期,& ...

  7. C# 正规表达式

    在C#中怎么用正则表达式限制文本框内不能输入数字?只能输入数字:"^[0-9]*$".只能输入n位的数字:"^\d{n}$".只能输入至少n位的数字:" ...

  8. js闭包(二)

    一.何谓“闭包”? 所谓“闭包(Closure)”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 描述的如此学术的官方解释,相信很少人能够 ...

  9. Solr搜索的排序打分规则探讨

    使用Solr搭建搜索引擎很容易,但是如何制定合理的打分规则(boost)做排序却是一个很头痛的事情.Solr本身的排序打分规则是继承自Lucene的文本相关度的打分即boost,这一套算法对于通用的提 ...

  10. go语言linux下安装

    1.从http://golang.org/dl/下载最新版本的GO语言二进制档案包. 注意:根据操作系统和计算架构正确选择档案包 2.使用tar命令将档案包解压到/usr/local目录中.具体方法如 ...