题目

输入格式

一行,一个由小写字母组成的字符串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

题解

BZOJ AC200纪念,,

这两题题干是一样的,但唯一不同的是。。后者卡空间【MLE得飞起】

先说解法:

我们知道后缀自动机上的parent树的每个节点子树中叶子的数量就是该节点表示的串的出现次数

显然我们要找的是子树叶子数为1,即代表出现次数为1的节点

parent树中的节点,要么是叶子节点,要么有至少两个儿子

由上面这个性质我们可以知道满足条件的只有叶子结点

所以我们连序都不用排了,直接统计不被父亲指针指向的节点

对于节点i,其表示的串为长度为\([step[pre[i]] + 1,step[i]]\)的串【pre为parent树父亲节点】

由于该串是唯一的,所以该串表示的最小串为后缀的子串都是唯一的

我们令其最小串为[l,r],其中\(l = step[i] - step[pre[i]],r = step[i]\)

我们分为两类:

①被最小串包含的位置[l,r],更新其最短识别长度为\(r - l + 1\)

②超出最小串的位置[1,l-1],更新其最短识别长度为\(r - i + 1\)【i为字符位置】

那么我们可以开两颗线段树分别维护①和②的最小值,最后输出时二者取最小即可【维护②的先不减i,输出时再减】

什么意思?每个位置会被两种形式的长度更新,①是一个值,②是一个值还要减去自身的位置,②不好处理,我们分开保存

T1就搞完了~~

等等,T2呢?

原来是数据范围变大了,改大,一交,雾草。。MLE

= =

何破?

①改用后缀数组【还没写】

②将ch改为map保存【好吧我就是这样A的】

T2太丢脸,贴T1代码吧,,

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
#define ls (u << 1)
#define rs (u << 1 | 1)
using namespace std;
const int maxn = 200005,maxm = 400005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
return out * flag;
}
int pre[maxn],ch[maxn][26],step[maxn],val[maxn],cnt,last,n;
char s[maxn];
void ins(int x){
int p = last,np = ++cnt;
last = np; step[np] = step[p] + 1;
while (p && !ch[p][x]) ch[p][x] = np,p = pre[p];
if (!p) pre[np] = 1;
else {
int q = ch[p][x];
if (step[q] == step[p] + 1) pre[np] = q;
else {
int nq = ++cnt; step[nq] = step[p] + 1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
pre[nq] = pre[q]; pre[q] = pre[np] = nq;
while (ch[p][x] == q) ch[p][x] = nq,p = pre[p];
}
}
}
struct Seg{
int mn[2 * maxn],tag[2 * maxn];
Seg(){for (int i = 0; i < maxm; i++) mn[i] = tag[i] = INF;}
void pd(int u){
if (tag[u] != INF){
mn[ls] = min(mn[ls],tag[u]); tag[ls] = min(tag[ls],tag[u]);
mn[rs] = min(mn[rs],tag[u]); tag[rs] = min(tag[rs],tag[u]);
tag[u] = INF;
}
}
void modify(int u,int l,int r,int L,int R,int v){
if (L > R) return;
if (l >= L && r <= R){
mn[u] = min(mn[u],v);
tag[u] = min(tag[u],v);
return;
}
pd(u);
int mid = l + r >> 1;
if (mid >= L) modify(ls,l,mid,L,R,v);
if (mid < R) modify(rs,mid + 1,r,L,R,v);
mn[u] = min(mn[ls],mn[rs]);
}
int query(int u,int l,int r,int pos){
if (l == r) return mn[u];
pd(u);
int mid = l + r >> 1;
if (mid >= pos) return query(ls,l,mid,pos);
else return query(rs,mid + 1,r,pos);
}
}A,B;
void solve(){
REP(i,cnt) val[i] = 1;
REP(i,cnt) val[pre[i]] = 0;
REP(i,cnt) if (val[i]){
int l = step[i] - step[pre[i]],r = step[i];
A.modify(1,1,n,l,r,r - l + 1);
B.modify(1,1,n,1,l - 1,r + 1);
}
REP(i,n)
printf("%d\n",min(A.query(1,1,n,i),B.query(1,1,n,i) - i));
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out1.txt","w",stdout);
scanf("%s",s + 1);
n = strlen(s + 1); cnt = last = 1;
REP(i,n) ins(s[i] - 'a');
solve();
return 0;
}

BZOJ1396&2865 识别子串 【后缀自动机 + 线段树】的更多相关文章

  1. bzoj1396&&2865 识别子串 后缀自动机+线段树

    Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample Input agoodco ...

  2. BZOJ 1396&&2865 识别子串[后缀自动机 线段树]

    Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如, ...

  3. 【BZOJ1396】识别子串 - 后缀自动机+线段树

    题意: Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. 题解: ...

  4. bzoj 1396/2865: 识别子串 后缀自动机+线段树

    水水的字符串题 ~ #include <map> #include <cstdio> #include <cstring> #include <algorit ...

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

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

  6. BZOJ 1396 识别子串 (后缀自动机+线段树)

    题目大意: 给你一个字符串S,求关于每个位置x的识别串T的最短长度,T必须满足覆盖x,且T在S中仅出现一次 神题 以节点x为结尾的识别串,必须满足它在$parent$树的子树中只有一个$endpos$ ...

  7. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  8. 2018.12.23 bzoj2865&&1396: 字符串识别(后缀自动机+线段树)

    传送门 卡空间差评! 题意简述:给一个字串,对于每个位置求出经过这个位置且只在字串中出现一次的子串的长度的最小值. 解法:先建出samsamsam,显然只有当sizep=1size_p=1sizep​ ...

  9. bzoj千题计划319:bzoj2865: 字符串识别(后缀自动机 + 线段树)

    https://www.lydsy.com/JudgeOnline/problem.php?id=2865 同上一篇博客 就是卡卡空间,数组改成map #include<map> #inc ...

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

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

随机推荐

  1. 安装Ubuntu桌面环境后只能Guest登录的解决办法

    1.安装Ubuntu桌面环境后,登录界面只显示了Guest 2.在登录界面按住crtl+shift+F1,进入tty模式 3.输入sudo -s进入root模式 4.输入vi /etc/lightdm ...

  2. http 调用错误处理

    1. http code 在使用Nginx时,经常会碰到502 Bad Gateway和504 Gateway Time-out错误,下面以Nginx+PHP-FPM来分析下这两种常见错误的原因和解决 ...

  3. vue 采坑

    1.ref 在父组件中访问子组件实例,或者直接操作DOM元素时需要ref <input ref="ipt"> 通过this.$refs.ipt 得到此input $re ...

  4. P4744 A’s problem(a)

    时间: 1000ms / 空间: 655360KiB / Java类名: Main 背景 冬令营入学测试题,每三天结算一次成绩.参与享优惠 描述 这是一道有背景的题目,小A也是一个有故事的人.但可惜的 ...

  5. GNU汇编 函数调用的例子

    .text .global  _start _start: mov r1,#2 cmp  r1,#1 bl func1    @bl能保存下一条指令的位置到lr寄存器里面,b不能 mov  r1, # ...

  6. Linux 系统性能

    Linux:PS命令详解与使用   要对进程进行监测和控制,首先必须要了解当前进程的情况,也就是需要查看当前进程,ps命令就是最基本进程查看命令.使用该命令可以确定有哪些进程正在运行和运行的状态.进程 ...

  7. 【JS】JS实现时间戳转换成普通时间

    var time = 1514457627; alert(getDate(time)); function getDate(tm){ var tt=new Date(parseInt(tm) * 10 ...

  8. keil swd设置下载stm32f103c8t6.

    1.debug选项,选择jlink,2.utilities选择jlink3.加载flash算法.4.选择swd模式,其他基本上默认,这样就可以下载了对rom和ram设置需要说明一下:1,IROM1,前 ...

  9. 指针的操作 p*++

    int x, y, *px = &x, *py = &y; y = *px + ; //表示把x的内容加5并赋给y,*px+5相当于(*px)+5 y = ++*px; //px的内容 ...

  10. python项目中输出指定颜色的日志

    起因 在开发项目过程中,为了方便调试代码,经常会向stdout中输出一些日志,默认的这些日志就直接显示在了终端中.而一般的应用服务器,第三方库,甚至服务器的一些通告也会在终端中显示,这样就搅乱了我们想 ...