识别子串 (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\)中出现的次数.(画一画就出来了) 然后直 ...
随机推荐
- MySQL 5.6 date 与 string 的转换和比较
我们有张表,表中有一个字段 dpt_date ,SQL 类型为 date,表示离开日期. 我们将 dpt_date 与字符串 ‘2016-03-09’ 进行比较,发现效率低于 dpt_date 转换为 ...
- 【转】 Pro Android学习笔记(八十):服务(5):访问远程服务
目录(?)[-] Client的AIDL文件 Client的代码 建立连接 请求服务 断开连接 文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://bl ...
- AngularJS:API
ylbtech-AngularJS:API 1.返回顶部 1. AngularJS API API 意为 Application Programming Interface(应用程序编程接口). An ...
- Python多进程-进程间数据的传递
两个进程间的数据是独立的,要进行数据传递的话可通过几个方法 Queue 通过队列来进行进程间数据的传递 # -*- coding:utf-8 -*- __author__ = "MuT6 S ...
- 玩school 学习sql server 查询的网站
http://www.w3school.com.cn/sql/sql_like.asp
- 如何利用MATLAB并行计算缩短程序运行时间
本来CPU就是双核,不过以前一直注重算法,没注意并行计算的问题.今天为了在8核的dell服务器上跑程序才专门看了一下.本身写的程序就很容易实现并行化,因为beamline之间并没有考虑相互作用.等于可 ...
- python NLTK 环境搭建
这里是我之前亲自操作过安装nltk,安装成功了.当时记得是参考这篇博文:http://www.tuicool.com/articles/VFf6Bza 其中,nltk安装时,遇到模块未找到,依次根据提 ...
- 离散对数的求解(bsgs)
bsgs算法 主要用来解决${A^x} = B(\bmod C)$(c是质数),都是整数,已知A.B.C求x. 例:poj 2417 Discrete Logging 具体步骤如下: 先把$x = i ...
- auth 权限控制
一. 权限介绍所谓权限控制,大部分是在管理后台上使用.比如超级管理员登录,会得到所有操作的控制权:认证专员,只能给会员做认证操作:审核专员,只能操作内容的审核.删除.加精等操作,以此类推.那么 Thi ...
- Vue02 样式的动态绑定
daigengxin......2018-3-8 21:09:18 跟angular2类似,分为CSS类绑定和Style样式绑定两种方式,详情参见