题链:

http://www.joyoi.cn/problem/tyvj-2301(非权限OI患者,苟且在joyoi。。。)
题解:

后缀自动机,线段树

先对原串建立后缀自动机,不难发现,
会影响答案是那些right集合大小恰好为1的状态。

考虑这些状态是如何影响答案的。
对于一个right集合大小为1的状态s,
令其允许的最大长度为maxs[s],其允许的最小长度为maxs[parent[s]]+1,其right集合里唯一的元素是minr(这里minr表示该状态对应的串在该位置结束)
我们可以得到对应的l=minr-maxs[s]+1,r=minr-maxs[parent[s]],即该状态对应的子串集就是S[l~r,minr]。
显然对于S[r~minr]这些字符,该状态可以给他们贡献一个maxs[parent[s]]+1的答案(对应着只出现过一次的子串S[r,minr])。
而对于S[l~r]这些字符,该状态可以给他们贡献的答案并不是一个相同的值,但是有一个共同点,
就是贡献给第i个字符的答案对应的子串是S[i,minr],即他们有着相同的结尾位置。
考虑到上面的两种贡献都是区间贡献,所以用线段树分别维护两种贡献即可。
(线段树的实现可以考虑先给线段树区间打上永久化标记,最后再通过一遍dfs得出每个叶子节点的最优答案。)

代码:

#include<bits/stdc++.h>
#define MAXN 100005
#define INF 0x3f3f3f3f
using namespace std;
struct SGT{
int rt,size;
int ls[MAXN*2],rs[MAXN*2],lazy[2][MAXN*2];
void Build(int &u,int l,int r){
u=++size;
lazy[0][u]=lazy[1][u]=INF;
if(l==r) return;
int mid=(l+r)/2;
Build(ls[u],l,mid);
Build(rs[u],mid+1,r);
}
void Modify(int u,int l,int r,int al,int ar,int val,int k){
if(al<=l&&r<=ar) return (void)(lazy[k][u]=min(lazy[k][u],val));
int mid=(l+r)/2;
if(al<=mid) Modify(ls[u],l,mid,al,ar,val,k);
if(mid<ar) Modify(rs[u],mid+1,r,al,ar,val,k);
}
void Answer(int u,int l,int r,int minlazy0,int minlazy1){
minlazy0=min(minlazy0,lazy[0][u]);
minlazy1=min(minlazy1,lazy[1][u]);
if(l==r){
printf("%d\n",min(minlazy0,minlazy1-l+1));
return;
}
int mid=(l+r)/2;
Answer(ls[u],l,mid,minlazy0,minlazy1);
Answer(rs[u],mid+1,r,minlazy0,minlazy1);
}
}DT;
struct SAM{
int size;
int maxs[MAXN*3],trans[MAXN*3][26],parent[MAXN*3],minr[MAXN*3],right[MAXN*3];
int Newnode(int a,int b){
++size; maxs[size]=a; minr[size]=INF;
memcpy(trans[size],trans[b],sizeof(trans[b]));
return size;
}
int Extend(int last,int x){
static int p,np,q,nq;
p=last; np=Newnode(maxs[p]+1,0);
for(;p&&!trans[p][x];p=parent[p]) trans[p][x]=np;
if(!p) parent[np]=1;
else{
q=trans[p][x];
if(maxs[p]+1!=maxs[q]){
nq=Newnode(maxs[p]+1,q);
parent[nq]=parent[q];
parent[q]=parent[np]=nq;
for(;p&&trans[p][x]==q;p=parent[p]) trans[p][x]=nq;
}
else parent[np]=q;
}
return np;
}
void Build(char *S){
static int p=1,last,len,tmp[MAXN],order[MAXN*3];
memset(trans[0],0,sizeof(trans[0]));
size=0; last=Newnode(0,0); len=strlen(S);
for(int i=0;i<len;i++) last=Extend(last,S[i]-'a');
for(int i=0;i<len;i++) p=trans[p][S[i]-'a'],minr[p]=i,right[p]=1;
for(int i=1;i<=size;i++) tmp[maxs[i]]++;
for(int i=1;i<=len;i++) tmp[i]+=tmp[i-1];
for(int i=1;i<=size;i++) order[tmp[maxs[i]]--]=i;
for(int i=size;i;i--){
p=order[i];
minr[parent[p]]=min(minr[parent[p]],minr[p]);
right[parent[p]]+=right[p];
}
}
void Solve(int len){
for(int i=1,l,r;i<=size;i++) if(right[i]==1){
l=minr[i]-maxs[i]+1; r=minr[i]-maxs[parent[i]];
DT.Modify(DT.rt,0,len-1,r,minr[i],maxs[parent[i]]+1,0);
DT.Modify(DT.rt,0,len-1,l,r,minr[i],1);
}
}
}SUF;
int main(){
static char S[MAXN];
scanf("%s",S);
int len=strlen(S);
DT.Build(DT.rt,0,len-1);
SUF.Build(S);
SUF.Solve(len);
DT.Answer(DT.rt,0,len-1,INF,INF);
return 0;
}

  

●BZOJ 1396 识别子串的更多相关文章

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

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

  2. BZOJ 1396:识别子串 SA+树状数组+单调队列

    1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 381  Solved: 243[Submit][Status][Discuss] ...

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

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

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

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

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

    手动博客搬家: 本文发表于20181221 00:58:26, 原地址https://blog.csdn.net/suncongbo/article/details/85150962 嗯,以后博客内容 ...

  6. bzoj 1396: 识别子串 && bzoj 2865: 字符串识别【后缀数组+线段树】

    根据height数组的定义,和当前后缀串i最长的相同串的长度就是max(height[i],height[i+1]),这个后缀贡献的最短不同串长度就是len=max(height[i],height[ ...

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

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

  8. bzoj 1396: 识别子串【SAM+线段树】

    建个SAM,符合要求的串显然是|right|==1的节点多代表的串,设si[i]为right集合大小,p[i]为right最大的r点,这些都可以建出SAM后再parent树上求得 然后对弈si[i]= ...

  9. BZOJ bzoj1396 识别子串

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

随机推荐

  1. 高级软件工程2017第3次作业——结对项目:四则运算题目生成程序(基于GUI)

    Deadline:2017-10-11(周三)21:00pm (注:以下内容参考集大作业 ) 前言 想过和别人一起探索世界吗?多么希望,遇到困难时,有人能一起探讨:想要懈怠时,有人推你一把:当你专注于 ...

  2. 敏捷开发每日报告--day4

    1 团队介绍 团队组成: PM:齐爽爽(258) 小组成员:马帅(248),何健(267),蔡凯峰(285)  Git链接:https://github.com/WHUSE2017/C-team 2 ...

  3. android 时间获取以及时间格式化

    Android中获取系统时间有多种方法,可分为Java中Calendar类获取,java.util.date类实现,还有android中Time实现 现总结如下: 方法一: void getTime1 ...

  4. JAVA_SE基础——29.构造函数

    黑马程序员入学Blog... jvm创建Java对象时候需要调用构造器,默认是不带参数的.在构造器中,你可以让jvm帮你初始化一些参数或者执行一系列的动作. 它是对象创建中执行的函数,及第一个被执行的 ...

  5. 电子称DIY(贴应变片+写代码)

    第一步.应变片介绍   ---------------------------------------------------------------------------------------- ...

  6. STM32读取温湿度传感器DHT11和DHT21(AM2301)系列问题

    1.DHT11和DHT21传感器 这两种传感器都是奥松公司的产品,具体的传感器说明书在其官网上有(www.aosong.com). DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合 ...

  7. Mego开发文档 - 复杂保存操作

    复杂保存操作 Mego框架还提供了更强大的数据更新API,以简化开发工作,同时也保证的性能. 指定属性添加数据 本列中指定插入一个数据对象,并且只会插入三列数据,最后两个属性是以表达式的形式插入. u ...

  8. api-gateway实践(01)服务网关 - 原型功能

    一.服务注册 1.增加组:LsqGrpA 2.增加版本:LsqVerA 3.增加api:LsqApiA 3.1.基本信息 3.2.前端定义 3.3.后端定义 二.服务上线和服务授权 1.服务上线 2. ...

  9. 刨析Maven(对pom.xml配置文件常用标签的解析)

    昨天在阿里云看到了一句话,"当你Learning和Trying之后,如果能尽量把Teaching也做好,会促进我们思考".共勉! 这是关于Maven的第三篇博客,这次我们深入了解p ...

  10. jscript定时器,一直用的东西,你真的明白吗?

    JavaScript定时器 JavaScript是一种解释型语言(边编译边执行),Js解析顺序是从上到下,然后将编译后的任务丢到一个事件队列中,然后事件内的函数会从上到下开始执行 setInterva ...