【BZOJ-1396&2865】识别子串&字符串识别 后缀自动机/后缀树组 + 线段树
1396: 识别子串
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 312 Solved: 193
[Submit][Status][Discuss]
Description

Input
Output
Sample Input
Sample Output
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】识别子串&字符串识别 后缀自动机/后缀树组 + 线段树的更多相关文章
- [模板] 后缀自动机&&后缀树
后缀自动机 后缀自动机是一种确定性有限状态自动机, 它可以接收字符串\(s\)的所有后缀. 构造, 性质 翻译自毛子俄罗斯神仙的博客, 讲的很好 后缀自动机详解 - DZYO的博客 - CSDN博客 ...
- poj 1743 Musical Theme 后缀自动机/后缀数组/后缀树
题目大意 直接用了hzwer的题意 题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题."主题&qu ...
- [BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】
题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log^2 n). 线段树比树状数组麻 ...
- dfs序+主席树 或者 树链剖分+主席树(没写) 或者 线段树套线段树 或者 线段树套splay 或者 线段树套树状数组 bzoj 4448
4448: [Scoi2015]情报传递 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 588 Solved: 308[Submit][Status ...
- POJ3080 POJ3450Corporate Identity(广义后缀自动机||后缀数组||KMP)
Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...
- [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)
[BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...
- BZOJ 1396&&2865 识别子串[后缀自动机 线段树]
Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如, ...
- BZOJ 1396||2865 识别子串
这个不是题解,看不懂的,别看了 明明应该是会的,怎么还是写了6个小时呢... 把后缀数组.height数组.排名数组求出来,那么对于原串s的任意子串[x,y](表示第x个到第y个字符组成的子串,字符从 ...
- BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)
题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...
随机推荐
- Testbench学习——$fopen/$display/$fclose
昨天在用Vivado写Testbench顶层时,为了以后便于数据的存储导出分析,需要用的文件数据记录的功能,于是,下面谈谈$fopen/$display/$fclose这三者的用法. $fopen—— ...
- mini2440的程序下载
mini2440拿到手有四天了,抱着很大的兴趣看韦东山老师的视频,但是因为电脑是win7 64bit的系统,dnw的驱动没有,经查询可以使用supervivi,就查找相关的资料.但是始终弄不好,后来使 ...
- Java代码优化总结
代码优化是一个很重要的课题.一般来说,代码优化的目标主要有两个,一个是减小代码的体积,另一个是提高代码运行的效率. 代码优化的细节有很多,此处列举部分: 1.尽量指定类.方法的final修饰符. 带有 ...
- geoserver 启动闪退
跟JDK版本有关: 比如geoserver2.11需要JDK版本为JDK1.8 windows配置两个jdk环境: 网上有方法,但如果只需要满足geoserver的话,可以只安装jdk(注意jdk和j ...
- Nginx - 压缩模块
1. 前言 在 Nginx 中与网页压缩相关的模块有两个:一个是 HttpGzipModule,另一个是 HttpGzipStaticModule.前者用于启用在文件传输过程中使用 gzip 压缩,而 ...
- 【Android开发日记】之入门篇(八)——Android数据存储(下)
废话不多说了,紧接着来讲数据库的操作吧.Come On! 提到数据存储问题,数据库是不得不提的.数据库是用来存储关系型数据的不二利器.Android为开发者提供了强大的数据库支持,可以用来轻松地构造基 ...
- opencv之dft及mat类型转换
跑实验时用到dft这个函数,根据教程,需要先将其扩充到最优尺寸,但我用逆变换后发现得到的mat的维数竟然不一样.因此还是不要扩展尺寸了. 参考:http://www.xpc-yx.com/2014/1 ...
- php rabbitmq的扩展
1.下载:https://github.com/alanxz/rabbitmq-c/archive/v0.9.0.tar.gz mkdir build && cd build # 这一 ...
- Luogu P2310 【loidc,看看海】
各位大佬都用的排序和杨颙大定理,蒟蒻的我怎么也不会做(瑟瑟发抖),那么,就来一发主席树吧.我们知道线段树可以维护区间,平衡树可以维护值域那么,我们可以用线段树套平衡树来解决这个区间值域的问题线段树套平 ...
- 20165203《Java程序设计》第四周学习总结
教材学习内容总结 第5章 子类与继承 子类的继承性 子类和父类在同一包中的继承性:子类继承父类中不是private的成员变量和方法作为自己的成员变量和方法 子类和父类不在同一包中的继承性:子类只继承父 ...