http://www.lydsy.com/JudgeOnline/problem.php?id=1396 (题目链接)

题意

  问字符串S每一位的最短识别子串是多长(识别子串指包含这个字符且只出现在S中一次的子串)。

Solution

  很简单,搞出后缀数组以后,对于每一个后缀i,都可以求出从i向后延伸的最短识别子串,也就是${max(height[rank[i]],height[rank[i]+1])+1}$,注意一种情况,就是i与排在它相邻位置的后缀的lcp就等于它自己的长度,这种情况i是没有向后延伸的识别子串的。

  所以正着求一遍后缀数组,反着求一遍后缀数组,然后线段树区间修改每个后缀的最短识别子串覆盖范围内的点就可以了。

细节

  太久没写线段树,都忘记要开4倍空间了→_→

代码

// bzoj1396
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=100010;
struct Segtree {int l,r,s,tag;}tr[maxn<<2];
int sa[maxn],height[maxn],rank[maxn];
char s[maxn]; namespace Suffix {
int wa[maxn],wb[maxn],ww[maxn];
bool cmp(int *r,int a,int b,int l) {
return r[a]==r[b] && r[a+l]==r[b+l];
}
void da(char *r,int *sa,int n,int m) {
int i,j,p,*x=wa,*y=wb;
for (i=0;i<=m;i++) ww[i]=0;
for (i=1;i<=n;i++) ww[x[i]=r[i]]++;
for (i=1;i<=m;i++) ww[i]+=ww[i-1];
for (i=n;i>=1;i--) sa[ww[x[i]]--]=i;
for (p=0,j=1;p<n;j*=2,m=p) {
for (p=0,i=n-j+1;i<=n;i++) y[++p]=i;
for (i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;
for (i=0;i<=m;i++) ww[i]=0;
for (i=1;i<=n;i++) ww[x[y[i]]]++;
for (i=1;i<=m;i++) ww[i]+=ww[i-1];
for (i=n;i>=1;i--) sa[ww[x[y[i]]]--]=y[i];
for (swap(x,y),p=x[sa[1]]=1,i=2;i<=n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j) ? p : ++p;
}
}
void calheight(char *r,int *sa,int n) {
for (int i=1;i<=n;i++) rank[sa[i]]=i;
for (int k=0,i=1;i<=n;i++) {
if (k) k--;
int j=sa[rank[i]-1];
while (r[i+k]==r[j+k]) k++;
height[rank[i]]=k;
}
}
} namespace SegTree {
void build(int k,int s,int t) {
tr[k].l=s,tr[k].r=t;
if (s==t) {tr[k].s=inf;return;}
int mid=(s+t)>>1;
if (s<=mid) build(k<<1,s,mid);
if (mid<t) build(k<<1|1,mid+1,t);
tr[k].s=min(tr[k<<1].s,tr[k<<1|1].s);
}
void pushdown(int k) {
int x=tr[k].tag;tr[k].tag=0;
if (x<tr[k<<1].s) tr[k<<1].s=tr[k<<1].tag=x;
if (x<tr[k<<1|1].s) tr[k<<1|1].s=tr[k<<1|1].tag=x;
}
void update(int k,int s,int t,int val) {
int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
if (l==s && r==t) {if (tr[k].s>val) tr[k].s=tr[k].tag=val;return;}
if (tr[k].tag) pushdown(k);
if (t<=mid) update(k<<1,s,t,val);
else if (s>mid) update(k<<1|1,s,t,val);
else update(k<<1,s,mid,val),update(k<<1|1,mid+1,t,val);
tr[k].s=max(tr[k<<1].s,tr[k<<1|1].s);
}
int query(int k,int p) {
int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
if (l==r && l==p) return tr[k].s;
if (p<=mid) return query(k<<1,p);
else return query(k<<1|1,p);
}
} int main() {
using namespace Suffix;
using namespace SegTree;
scanf("%s",s+1);
int n=strlen(s+1);
da(s,sa,n,300);
calheight(s,sa,n);
build(1,1,n);
for (int i=1;i<=n;i++) {
int val=max(height[rank[i]],height[rank[i]+1]);
if (val==n-i+1) continue;
update(1,i,i+val,val+1);
}
for (int i=1;i<=n/2;i++) swap(s[i],s[n-i+1]);
da(s,sa,n,300);
calheight(s,sa,n);
for (int i=1;i<=n;i++) {
int val=max(height[rank[i]],height[rank[i]+1]);
if (val==n-i+1) continue;
update(1,n-(i+val)+1,n-i+1,val+1);
}
for (int i=1;i<=n;i++) printf("%d\n",min(n,query(1,i)));
return 0;
}

【bzoj1396】 识别子串的更多相关文章

  1. BZOJ1396 识别子串【SAM+SegmentTree】

    BZOJ1396 识别子串 给定一个串\(s\),对于串中的每个位置,输出经过这个位置且只在\(s\)中出现一次的子串的最短长度 朴素的想法是,我们要找到那些只出现一次的子串,之后遍历每个串,把串所覆 ...

  2. bzoj千题计划318:bzoj1396: 识别子串(后缀自动机 + 线段树)

    https://www.lydsy.com/JudgeOnline/problem.php?id=1396 后缀自动机的parent树上,如果不是叶子节点,那么至少有两个子节点 而一个状态所代表子串的 ...

  3. BZOJ1396:识别子串(SAM)

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

  4. BZOJ-1396: 识别子串

    后缀自动机+线段树 先建出\(sam\),统计一遍每个点的\(right\)集合大小\(siz\),对于\(siz=1\)的点\(x\),他所代表的子串只会出现一次,设\(y=fa[x]\),则这个点 ...

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

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

  6. BZOJ1396 识别子串 和 BZOJ2865 字符串识别

    字符串识别 2865: 字符串识别 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 839  Solved: 261[Submit][Status][D ...

  7. BZOJ1396 识别子串 字符串 SAM 线段树

    原文链接http://www.cnblogs.com/zhouzhendong/p/9004467.html 题目传送门 - BZOJ1396 题意 给定一个字符串$s$,$|s|\leq 10^5$ ...

  8. BZOJ bzoj1396 识别子串

    题面: bzoj1396 题解: 先建出SAM,并计算right集合大小.显然符合条件的点的right集合大小为1. 对于每个right集合为1的状态显然可以算出这些状态的pos以及maxlen和mi ...

  9. bzoj1396: 识别子串

    #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...

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

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

随机推荐

  1. 配置tensorflow环境(anaconda+jupyter notebook)

    很早之前,tensorflow环境之前我也曾装过,但是用的不是很舒服,很多问题都不明所以然.今天想要系统地学习一下tensorflow,于是又重新搭建了一遍,这次还是踩了不少坑.特此写下此文,供有兴趣 ...

  2. PHP中的__toString() 是什么东西

    __toString()  是魔术方法的一种,具体用途是当一个对象被当作字符串对待的时候,会触发这个魔术方法 以下说明摘自PHP官方手册 public string __toString ( void ...

  3. java浮点数存储

    转自: [解惑]剖析float型的内存存储和精度丢失问题 1.小数的二进制表示问题 首先我们要搞清楚下面两个问题: (1)  十进制整数如何转化为二进制数 算法很简单.举个例子,11表示成二进制数: ...

  4. babel无法编译?

    ECMAScript 6(ES6)的发展或者说普及之快可以说是难以想象的,对很多人来说ECMAScript 5(ES5)都还普及呢.现代浏览器对ES6新特新或多或少的有些支持,但支持度不高,所以要想在 ...

  5. 作业要求 20181127-5 Beta发布用户使用报告

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2450 一.用户反馈 反馈截图(部分) 三.用户反馈情况统计图

  6. linux简单命令常用随记

    //查看网络信息 ifconfig //修改ip地址 ifconfig eth0 123.123.123.123 netmask 255.255.255.0 //网关设置 route add defa ...

  7. 树莓派3+rtl8812au开启monitor模式

    首先要有一块树莓派,要有一块rtl8812au的网卡. 这个网卡是支持monitor模式的,但是我原来装的驱动驱动在raspbian上开启monitor模式时提示,找不到设备. 然后换了一个驱动 ht ...

  8. 始入OO课程的殿堂,初识面向对象的奥妙——OO第一次博客总结

    当我满怀期待叩开OO的大门,却发现宝藏藏在层层阻难之后 第一次作业 1.度量分析 >关于第一次作业的metrics图分析没有出现标红的McCabe Cyclomatic Complexity或者 ...

  9. YQCB冲刺周第六天

    站立会议如上图 任务看板: 今天的任务为依旧为将用户记录的数据添加到数据库中,以及金额球的设置. 遇到的问题为金额球在jsp页面的显示.

  10. “Gogoing”改进方案

    通过看见他们对我们团队的意见点评,我们还要有更多改善的地方. 首先,就是界面的优化: 其次,加上自己些特有的功能,吸引更多的用户: 然后,需要整理大量的数据库信息才能完善: 最后,需要有其他软件的集成 ...