1396: 识别子串

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 312  Solved: 193
[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

HINT

Source

Solution

BZOJ:1396

这题使用 后缀自动机+线段树 的做法;

首先按照这道题的要求,所有的识别子串一定是$|Right|=1$的节点所代表的子串之一,问题在于统计最短。

这样可以考虑再记录每个$Right$集合的最后一个$endpos$,然后利用这个去考虑对原串每个位置的贡献。

对于$[endpos-maxlen+1,endpos-minlen+1]$中的每个位置$x$,贡献就是$endpos-x+1$;

理解起来就是对于$[endpos-maxlen+1,endpos-minlen+1]$中的每个子串都是单独存在的,所以这个$Right$集合贡献给他的答案就是这个点到$endpos$的长度。

对于$[endpos-minlen,endpos]$中的每个位置$x$,贡献就是$minlen$;

理解起来就是对于$[endpos-minlen,endpos]$中的每个子串显然是存在多个结束位置的,所以当前这个$Right$集合能够贡献给他的符合只出现一次且最短的,就是刚好跨过它的最小,即$minlen$。

这样对于两种情况,分别用线段树去维护,至于第一种情况的$-x$是定值,所以拿到外面比较的时候再算进答案即可。

BZOJ2865

这道题和上道题完全一样,不过数据范围扩大了,需要卡内存...用上了map内存还是多10M...于是直接写 后缀树组+线段树 的做法了。

和刚刚的想法类似。

考虑固定左端点$l$,对后面的影响。

容易发现,固定了$l$之后,对后面的答案,存在两种影响。

设这样两种情况的分界点为$m$

对于第一种影响$[l,m]$贡献的答案就是$lcp+1$,而第二种$[m+1,N]$的每个位置$x$贡献答案就是$x-l+1$

所以这样可以枚举$sa_{i}$,对于$sa_{i}$它的$lcp+1$实际上就是与它相邻的两个后缀的$height_{max}+1$,那么分界点$m$恰好是$sa_{i}+lcp$.

理解起来就是对于它的$lcp$显然是重复出现过的,那么这段的答案最短就是$lcp+1$,而对于这段之后的位置的影响,显然就是 那个位置到当前位置$sa_{i}$的距离长度的串 最短。

用同样的方法用两棵线段树维护.

Code

BZOJ1396

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define MAXN 100010
#define INF 0x7fffffff
char S[MAXN];
int N;
namespace SegmentTree{
#define lson now<<1
#define rson now<<1|1
struct SgtNode{
int l,r,minn,tag;
};
struct SegmentTree{
SgtNode tree[MAXN<<2];
inline void Update(int now) {tree[now].minn=min(tree[lson].minn,tree[rson].minn);}
inline void Build(int now,int l,int r)
{
tree[now].tag=tree[now].minn=INF;
tree[now].l=l,tree[now].r=r;
if (l==r) return;
int mid=(l+r)>>1;
Build(lson,l,mid); Build(rson,mid+1,r);
}
inline void Pushdown(int now)
{
if (tree[now].l==tree[now].r || tree[now].tag==INF) return;
int val=tree[now].tag;
tree[now].tag=INF;
tree[lson].minn=min(tree[lson].minn,val);
tree[rson].minn=min(tree[rson].minn,val);
tree[lson].tag=min(tree[lson].tag,val);
tree[rson].tag=min(tree[rson].tag,val);
}
inline void Modify(int now,int L,int R,int val)
{
if (L>R) return;
int l=tree[now].l,r=tree[now].r;
Pushdown(now);
if (L<=l && R>=r) {
tree[now].tag=min(tree[now].tag,val);
tree[now].minn=min(tree[now].minn,val);
return;
}
int mid=(l+r)>>1;
if (L<=mid) Modify(lson,L,R,val);
if (R>mid) Modify(rson,L,R,val);
Update(now);
}
inline int Query(int now,int pos)
{
int l=tree[now].l,r=tree[now].r;
Pushdown(now);
if (l==r) return tree[now].minn;
int mid=(l+r)>>1;
if (pos<=mid) return Query(lson,pos);
else return Query(rson,pos);
}
}t1,t2;
}using namespace SegmentTree; namespace SAM{
int son[MAXN<<1][27],len[MAXN<<1],par[MAXN<<1],endpos[MAXN<<1],size[MAXN<<1];
int last,root,sz;
inline void init() {last=root=++sz;}
inline void Extend(int c,int pos)
{
int cur=++sz,p=last;
len[cur]=len[p]+1; endpos[cur]=pos; size[cur]=1;
while (p && !son[p][c]) son[p][c]=cur,p=par[p];
if (!p) par[cur]=root;
else {
int q=son[p][c];
if (len[p]+1==len[q]) par[cur]=q;
else {
int nq=++sz;
memcpy(son[nq],son[q],sizeof(son[nq]));
len[nq]=len[p]+1; par[nq]=par[q];
while (p && son[p][c]==q) son[p][c]=nq,p=par[p];
par[q]=par[cur]=nq;
}
}
last=cur;
}
inline void Build() {init(); for (int i=1; i<=N; i++) Extend(S[i]-'a'+1,i);}
int st[MAXN],id[MAXN<<1];
inline void Prework()
{
for (int i=1; i<=sz; i++) st[len[i]]++;
for (int i=1; i<=N; i++) st[i]+=st[i-1];
for (int i=1; i<=sz; i++) id[st[len[i]]--]=i;
for (int i=sz; i>=1; i--) {
int x=id[i];
size[par[x]]+=size[x];
endpos[par[x]]=max(endpos[par[x]],endpos[x]);
}
}
inline void Work()
{
for (int i=1; i<=sz; i++)
if (size[i]==1) {
int maxlen=len[i],minlen=len[par[i]]+1,end=endpos[i];
// printf("%d %d %d\n",end,maxlen,minlen);
t1.Modify(1,end-maxlen+1,end-minlen+1,end+1);
t2.Modify(1,end-minlen+1,end,minlen);
} for (int i=1; i<=N; i++) {
int x=t1.Query(1,i)-i,y=t2.Query(1,i);
printf("%d\n",min(x,y));
}
}
}using namespace SAM;
int main()
{
scanf("%s",S+1); N=strlen(S+1); t1.Build(1,1,N),t2.Build(1,1,N); SAM::Build();
SAM::Prework();
SAM::Work(); return 0;
}

BZOJ2865

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MAXN 500010
char S[MAXN];
int N;
#define INF 0x3fffffff
namespace SegmentTree{
#define lson now<<1
#define rson now<<1|1
struct SgtNode{
int l,r,minn,tag;
};
struct SegmentTree{
SgtNode tree[MAXN<<2];
inline void Update(int now) {tree[now].minn=min(tree[lson].minn,tree[rson].minn);}
inline void Build(int now,int l,int r)
{
tree[now].tag=tree[now].minn=INF;
tree[now].l=l,tree[now].r=r;
if (l==r) return;
int mid=(l+r)>>1;
Build(lson,l,mid); Build(rson,mid+1,r);
}
inline void Pushdown(int now)
{
if (tree[now].l==tree[now].r || tree[now].tag==INF) return;
int val=tree[now].tag;
tree[now].tag=INF;
tree[lson].minn=min(tree[lson].minn,val);
tree[rson].minn=min(tree[rson].minn,val);
tree[lson].tag=min(tree[lson].tag,val);
tree[rson].tag=min(tree[rson].tag,val);
}
inline void Modify(int now,int L,int R,int val)
{
if (L>R) return;
int l=tree[now].l,r=tree[now].r;
Pushdown(now);
if (L<=l && R>=r) {
tree[now].tag=min(tree[now].tag,val);
tree[now].minn=min(tree[now].minn,val);
return;
}
int mid=(l+r)>>1;
if (L<=mid) Modify(lson,L,R,val);
if (R>mid) Modify(rson,L,R,val);
Update(now);
}
inline int Query(int now,int pos)
{
int l=tree[now].l,r=tree[now].r;
Pushdown(now);
if (l==r) return tree[now].minn;
int mid=(l+r)>>1;
if (pos<=mid) return Query(lson,pos);
else return Query(rson,pos);
}
}t1,t2;
}using namespace SegmentTree; namespace SA{
int r[MAXN],sa[MAXN],rank[MAXN],height[MAXN],st[MAXN],tx[MAXN],ty[MAXN];
inline void Sort(int *x,int *y,int *sa,int L,int M)
{
for (int i=0; i<=M; i++) st[i]=0;
for (int i=0; i<L; i++) st[x[y[i]]]++;
for (int i=1; i<=M; i++) st[i]+=st[i-1];
for (int i=L-1; i>=0; i--) sa[--st[x[y[i]]]]=y[i];
}
inline void DA(int *r,int *sa,int L,int M)
{
int *x=tx,*y=ty,*t,i,j,p;
for (int i=0; i<L; i++) x[i]=r[i],y[i]=i;
Sort(x,y,sa,L,M);
for (j=1,p=1; j<L && p<L; j<<=1,M=p-1) {
for (p=0,i=L-j; i<L; i++) y[p++]=i;
for (i=0; i<=L; i++) if (sa[i]>=j) y[p++]=sa[i]-j;
Sort(x,y,sa,L,M);
for (t=x,x=y,y=t,x[sa[0]]=0,i=1,p=1; i<L; i++)
x[sa[i]]=y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]? p-1:p++;
}
}
inline void Height(int *r,int *sa,int *rank,int *h,int L)
{
h[1]=0;
for (int i=1; i<=L; i++) rank[sa[i]]=i;
for (int i=1,j,k=0; i<=L; h[rank[i++]]=k)
for (k? k--:k=0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);
}
}using namespace SA; int main()
{
scanf("%s",S+1); N=strlen(S+1);
for (int i=1; i<=N; i++) r[i]=S[i]-'a'+1;
SA::DA(r,sa,N+1,27);
SA::Height(r,sa,rank,height,N); // for (int i=1; i<=N; i++) printf("%d ",sa[i]); puts("");
// for (int i=1; i<=N; i++) printf("%d ",height[i]); puts(""); t1.Build(1,1,N); t2.Build(1,1,N); for (int i=1; i<=N; i++) {
int j=max(height[i],height[i+1])+1;
if (j+sa[i]-1<=N) t1.Modify(1,sa[i],sa[i]+j-1,j);
t2.Modify(1,sa[i]+j,N,1-sa[i]);
// printf("%d %d %d %d\n",i,j,sa[i],j+sa[i]);
}
for (int i=1; i<=N; i++) {
int x=t1.Query(1,i),y=t2.Query(1,i)+i;
printf("%d\n",min(x,y));
}
return 0;
}

自己断断续续想了好久才想到...真的是弱的没救了....

【BZOJ-1396&2865】识别子串&字符串识别 后缀自动机/后缀树组 + 线段树的更多相关文章

  1. [模板] 后缀自动机&&后缀树

    后缀自动机 后缀自动机是一种确定性有限状态自动机, 它可以接收字符串\(s\)的所有后缀. 构造, 性质 翻译自毛子俄罗斯神仙的博客, 讲的很好 后缀自动机详解 - DZYO的博客 - CSDN博客 ...

  2. poj 1743 Musical Theme 后缀自动机/后缀数组/后缀树

    题目大意 直接用了hzwer的题意 题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题."主题&qu ...

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

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

  4. dfs序+主席树 或者 树链剖分+主席树(没写) 或者 线段树套线段树 或者 线段树套splay 或者 线段树套树状数组 bzoj 4448

    4448: [Scoi2015]情报传递 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 588  Solved: 308[Submit][Status ...

  5. POJ3080 POJ3450Corporate Identity(广义后缀自动机||后缀数组||KMP)

    Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...

  6. [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)

    [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...

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

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

  8. BZOJ 1396||2865 识别子串

    这个不是题解,看不懂的,别看了 明明应该是会的,怎么还是写了6个小时呢... 把后缀数组.height数组.排名数组求出来,那么对于原串s的任意子串[x,y](表示第x个到第y个字符组成的子串,字符从 ...

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

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

随机推荐

  1. JDK1.8源码ArrayList

    线程不安全的,如果要想线程安全必须在创建的时候就采用线程安全的方式创建: List list = Collections.synchronizedList(new ArrayList(...)); 引 ...

  2. 新手向-同步关键字synchronized对this、class、object、方法的区别

    synchronized的语义 实验 分析 在看源代码时遇到多线程需要同步的时候,总是会看见几种写法,修饰方法.修饰静态方法.synchronized(Xxx.class).synchronized( ...

  3. 80.YCrCb - YUV - RGB之间的介绍

    一,引言 YUV(亦称YCrCb)是被欧洲电视系统所采用的一种颜色编码方法(属于PAL).YUV主要用于优化彩色视频信号的传输,使其向后兼容老式黑白电视.与RGB视频信号传输相比,它最大的优点在于只需 ...

  4. Error: No resource found that matches the given name (at 'icon' with value '@mipmap/Icon')

    问题: error: Error: No resource found that matches the given name (at 'icon' with value '@mipmap/Icon' ...

  5. 10 The Go Programming Language Specification go语言规范 重点

    The Go Programming Language Specification go语言规范 Version of May 9, 2018 Introduction 介绍 Notation 符号 ...

  6. 04 Effective Go 高效的go语言 重点内容

    Effective Go  高效的go语言 Introduction 介绍 Examples 例子 Formatting 格式 Commentary 评论 Names 名字 Package names ...

  7. jQuery中绑定事件的几种方法

    以click事件为例,jQuery中绑定事件有三种方法: (1)target.click(function(){});  (2)target.bind("click",functi ...

  8. C语言位域——精妙使用内存

    参考链接  https://blog.csdn.net/yanbober/article/details/8697967  https://blog.csdn.net/Tommy_wxie/artic ...

  9. JAVA随笔(三)

    私有是针对类的,而不是对象. static 函数,其实是类函数.之前一直不太理解每个类中的static main是什么意思,为什么main中不能直接调用非静态的变量:因为main是 类函数,不是属于某 ...

  10. C++链接与装载

    1..obj文件的内部结构 2.映射到进程虚拟空间 3.链接的原理    C++ Code  123456789   1.未解决符号表:提供了所有在该编译单元里引用但是定义并不在本编译单元里的符号及其 ...