识别子串 (string)——后缀自动机+线段树
题目
【题目描述】
一般地,对于一个字符串 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)——后缀自动机+线段树的更多相关文章
- bzoj千题计划318:bzoj1396: 识别子串(后缀自动机 + 线段树)
https://www.lydsy.com/JudgeOnline/problem.php?id=1396 后缀自动机的parent树上,如果不是叶子节点,那么至少有两个子节点 而一个状态所代表子串的 ...
- BZOJ1396&2865 识别子串 【后缀自动机 + 线段树】
题目 输入格式 一行,一个由小写字母组成的字符串S,长度不超过10^5 输出格式 L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. 输入样例 agoodcookcooksg ...
- BZOJ1396: 识别子串(后缀自动机 线段树)
题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
随机推荐
- Java-API:java.util.Date
ylbtech-Java-API:java.util.Date Module java.base Package java.util Class Date java.lang.Object java. ...
- SQL属性第一个值不被选中,属性默认第一个值
把 Please Choose Color 属性名设置为不可选的 UPDATE `products_attributes` SET `attributes_display_only` = '1' WH ...
- Python unittest excel数据驱动 写入
之前写过一篇关于获取excel数据进行迭代的方法,今天补充上写入的方法.由于我用的是Python3,不兼容xlutils,所以无法使用copy excel的方式来写入.这里使用xlwt3创建excel ...
- Solaris与Windows Active Directory集成
通过Solaris与Active Directory的集成,Solaris可以使用Windows 2003 R2/ 2008 Active Directory来进行用户登录验证.以下是简要配置过程. ...
- Spring注解-TaskScheduler
一.定义配置类 import org.springframework.context.annotation.ComponentScan; import org.springframework.cont ...
- java中的自动转型的学习理解
java当中的继承是和c++中的继承类似,只是java中的继承时的父类只能有一位. 我们今天在这里讲的是关于java中的自动转型的理解:顾名思义,自动转型值得就是使用时自动的将自身的类型进行转化. 自 ...
- 使用GSON来生成JSON数据
第二种方法: 当不需要显示某个属性时,在不需要显示出的属性前加transient关键字即可满足 使用gson来解析 使用gson解析 带日期转换 集合类解析:gson中的数组与java中集合类都是对应 ...
- cfree使用cygwin编译程序出现计算机丢失cygwin1.dll解决办法
这种情况多是环境没配好,我的是64位cygwin C:\cygwin64\bin 加入到环境变量中,重打开cfree就可以解决.
- 指定jdk编译或运行
set JAVA_HOME=D:\java\jdk8 set CLASSPATH=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOMe%\lib\tools.jar; set Pat ...
- PLSQL语法
Procedural Language和SQL的结合体.通过增加变量.控制语句,使我们可以写些逻辑更加复杂的数据库操作 语句框架组成 declare – 可选 声明各种变量或游标的地方. begin ...