1396: 识别子串

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 381  Solved: 243
[Submit][Status][Discuss]

Description

Input

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

Output

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

Sample Input

agoodcookcooksgoodfood

Sample Output

1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4
 
 
想法:
如果s[i..j]为识别串,s[i-y..j+x]同样也是识别串,可以通过枚举一个端点来得到最短识别串。
设wi=max{lcp(i,j)}+1=max{height[rank[i]],height[rank[i+1]]}+1
s[i..j]为识别子串<==>j-i+1>=wi。
即j>=i+max{height[rank[i]],height[rank[i+1]]},令hi=i+max{height[rank[i]],height[rank[i+1]]}
得到ansi=min{max(j-i+1,hi-i+1)}i<=j,分开求:
①ansj=min(j-i+1)hi<=j;树状数组维护
②ansj=min(hi-i+1)i<=j,hi>=j;线段树维护[i,hi]的区间最值。
显然h(i)>h(i-1),当 hx-x<hy-y&&x>y,y就无用了,于是使用单调队列。
O(nlogn)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
const int len(100000),limt(255);
int str[len+10],sfa[len+10],rank[len+10],height[len+10];
int tmp[len+10],p[len+10],cnt[len+10];
int n,c[len+10],ans[len+10],q[len+10],li,hi;char ch[len+10];
struct data{int x,y;}h[len+10];
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
bool com(int x,int y,int l){return (rank[x]==rank[y])&&(rank[x+l]==rank[y+l]);}
void doubling()
{
for(int i=1;i<=n;i++){rank[i]=str[i];sfa[i]=i;}
for(int pos=0,l=0,sigma=limt;pos<n;sigma=pos)
{
pos=0;
for(int i=n-l+1;i<=n;i++)p[++pos]=i;
for(int i=1;i<=n;i++)if(sfa[i]>l)p[++pos]=sfa[i]-l;
memset(cnt,0,sizeof(int)*(sigma+1));pos=0;
for(int i=1;i<=n;i++)cnt[rank[i]]++;
for(int i=1;i<=sigma;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sfa[cnt[rank[p[i]]]--]=p[i];
for(int i=1;i<=n;i++)tmp[sfa[i]]=com(sfa[i],sfa[i-1],l)?pos:++pos;
for(int i=1;i<=n;i++)rank[i]=tmp[i];
l=!l?1:l<<1;
}
for(int i=1;i<=n;i++)rank[sfa[i]]=i;
for(int i=1,k,j;i<=n;i++)
{
k=sfa[rank[i]-1];
if(!k)continue;
j=height[rank[i-1]];
if(j)j--;
while(str[i+j]==str[k+j])j++;
height[rank[i]]=j;
}
}
int query(int x){int sum=0;for(;x;x-=x&(-x))sum=max(sum,c[x]);return sum;}
void put(int x,int y){for(;x<=n;x+=x&(-x))c[x]=max(c[x],y);}
int main()
{
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
scanf("%s",ch);n=strlen(ch);
for(int i=1;i<=n;i++)str[i]=ch[i-1]-'a'+1;
doubling();
for(int i=1;i<=n;i++)
{
h[i].x=i+max(height[rank[i]],height[rank[i]+1]);
h[i].y=i;
if(h[i].x>n+1)printf("NO");
}
for(int i=1,t;i<=n;i++)
{
if(h[i].x!=n+1)ans[h[i].y]=h[i].x-h[i].y+1;
else ans[h[i].y]=n;
t=query(h[i].y);
if(t)ans[h[i].y]=min(h[i].y-t+1,ans[h[i].y]);
if(h[i].x!=n+1)put(h[i].x,h[i].y);
}
q[li=1]=1;hi=1;
for(int i=2;i<=n;i++)
{
while(h[q[hi]].x<i&&hi<=li)hi++;
if(hi<=li&&h[q[hi]].x>=i&&h[q[hi]].x!=n+1)ans[h[i].y]=min(ans[h[i].y],h[q[hi]].x-h[q[hi]].y+1);
if(h[i].x!=n+1)
{
while(h[q[li]].x-h[q[li]].y>h[i].x-h[i].y&&li>=hi)li--;
q[++li]=i;
}
}
for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
return 0;
}

  

BZOJ 1396:识别子串 SA+树状数组+单调队列的更多相关文章

  1. 大视野 1012: [JSOI2008]最大数maxnumber(线段树/ 树状数组/ 单调队列/ 单调栈/ rmq)

    1012: [JSOI2008]最大数maxnumber Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 9851  Solved: 4318[Submi ...

  2. BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)

    题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...

  3. bzoj 1396 识别子串 后缀树+线段树

    题目大意 给定一个长度\(\le100000\)的字符串 求每一个位置的最短识别子串 对于位置\(x\),能识别子串\(s[i...j]\)的条件是 1.\(i\le x \le j\) 2.\(s[ ...

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

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

  5. bzoj 3262 陌上花开 - CDQ分治 - 树状数组

    Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当 ...

  6. BZOJ 1901 Zju2112 Dynamic Rankings ——树状数组套主席树

    [题目分析] BZOJ这个题目抄的挺霸气. 主席树是第一时间想到的,但是修改又很麻烦. 看了别人的题解,原来还是可以用均摊的思想,用树状数组套主席树. 学到了新的姿势,2333o(* ̄▽ ̄*)ブ [代 ...

  7. [BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

    题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log^2 n). 线段树比树状数组麻 ...

  8. BZOJ 2743: [HEOI2012]采花 [树状数组 | 主席树]

    题意: 查询区间中出现次数$>2$的颜色个数 一眼主席树,区间中$l \le last[i] \le r$的个数减去$l \le last[last[i]] \le r$的个数,搞两颗主席树来做 ...

  9. BZOJ.4826.[AHOI/HNOI2017]影魔(树状数组/莫队 单调栈)

    BZOJ LOJ 洛谷 之前看\(mjt\)用莫队写了,以为是一种正解,码了3h结果在LOJ T了没A= = 心态爆炸(upd:发现是用C++11(NOI)交的,用C++11交就快一倍了...) 深刻 ...

随机推荐

  1. Socket()与WSASocket()的区别

    socket()   创建一个通讯端点并返回一个套接口.但是在socket库中例程在应用于阻塞套接口时会阻塞.     WSASocket()的发送操作和接收操作都可以被重叠使用.接收函数可以被多次调 ...

  2. C++ 无边框的拖动窗口代码

    按下鼠标时,记录下鼠标在窗体上的坐标, 同时用一个BOOL变量记录下鼠标左键按下的状态 弹起左键,则记录状态的布尔变量为FALSE. 处理鼠标移动事件,如果左键同时按下,则为鼠标拖动窗体运动,设置窗体 ...

  3. 洛谷 - P3952 - 时间复杂度 - 模拟

    https://www.luogu.org/problemnew/show/P3952 这个模拟,注意每次进入循环的时候把新状态全部入栈,退出循环的时候就退栈. 第一次就错在发现ERR退出太及时,把剩 ...

  4. linux bg和fg命令

    linux下我们如果想一个任务或者程序还后台执行可以使用&,实际上linux还提供了其他任务调度的命令. bg将一个在后台暂停的命令,变成继续执行 fg将后台中的命令调至前台继续运行 jobs ...

  5. 从零开始学Docker

    在写这篇博客之前,听说过Docker技术,但是一直没有主动去深入了解.用这篇博客来记录自己学习Docker的个人总结,会一直补充完善. 我这边先随便写,后期再做总结!! Docker官网: https ...

  6. centos 创建 logrotate 进行日志分割

    这里就不赘述logrotate了,具体是什么,有什么作用,自行百度. 我们先说下,如何进行nginx的日志切割: 比如:日志目录为:/usr/local/nginx/logs/access.log按照 ...

  7. Codeforces 161E(搜索)

    要点 标签是dp但搜索一发就能过了. 因为是对称矩阵所以试填一下就是一个外层都填满了,因此搜索的深度其实不超过5. 显然要预处理有哪些素数.在这个过程中可以顺便再处理出一个\(vector:re[le ...

  8. HDU1854 Q-Sequence

    http://acm.hdu.edu.cn/showproblem.php?pid=1854 随手练习 #include <bits/stdc++.h> using namespace s ...

  9. 前端HTML(二/三)

    待补充 一.字体标签 字体标签包含:h1~h6.<font>.<u>.<b>.<strong>.<em>.<sup>.<s ...

  10. 命令行下载工具 wget

    wget 是一个简单而强大的跨平台命令行下载工具,包括 Windows 也有对应的版本.全称 GNU Wget,属于 GNU 计划的一部分,自由软件.支持 HTTP.HTTPS 和 FTP 协议,可在 ...