bzoj 1396 识别子串 后缀树+线段树
题目大意
给定一个长度\(\le100000\)的字符串
求每一个位置的最短识别子串
对于位置\(x\),能识别子串\(s[i...j]\)的条件是
1.\(i\le x \le j\)
2.\(s[i...j]\)在原串中只出现了一次
分析
从第二个条件入手
仅出现一次子串就是后缀树上\(|right|=1\)的子串
考虑贡献
该后缀左端点在\(left\)
长度范围\([L,R]\)
如图
对于\(A\)部分贡献的最短串长度为\(L\)
对于\(B\)部分贡献是等差数列\(i-lef+1\),提出\(1-lef\)
对上面分两棵线段树维护即可
solution
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
using namespace std;
const int M=200007;
const int INF=1e9+7;
struct edge{int y,nxt;};
struct vec{
int g[M],te;
edge e[M];
vec(){memset(g,0,sizeof(g));te=0;}
void clear(){memset(g,0,sizeof(g));te=0;}
inline void push(int x,int y){e[++te].y=y;e[te].nxt=g[x];g[x]=te;}
inline int& operator () (int x){return g[x];}
inline edge& operator [] (int x){return e[x];}
}go;
char s[M];
int n;
int ch[M][26];
int stp[M],fa[M];
int lef[M];
int last,tot;
int ans[M];
struct Seg{
int tag;
Seg(){tag=INF;}
}seg1[262147],seg2[262147];
int newnode(int ss){
stp[++tot]=ss;
return tot;
}
int ext(int p,int q,int d){
int nq=newnode(stp[p]+1);
fa[nq]=fa[q]; fa[q]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
for(;p&&ch[p][d]==q;p=fa[p]) ch[p][d]=nq;
return nq;
}
int sam(int p,int d){
int np=ch[p][d];
if(np) return (stp[p]+1==stp[np]) ? np : ext(p,np,d);
np=newnode(stp[p]+1);
for(;p&&!ch[p][d];p=fa[p]) ch[p][d]=np;
if(!p) fa[np]=1;
else{
int q=ch[p][d];
fa[np]= (stp[p]+1==stp[q]) ? q : ext(p,q,d);
}
return np;
}
void mdf(Seg *S,int x,int l,int r,int tl,int tr,int d){
if(S[x].tag<=d) return;
if(tl<=l&&r<=tr) {S[x].tag=d;return;}
int mid=l+r>>1;
if(tl<=mid) mdf(S,x<<1,l,mid,tl,tr,d);
if(mid<tr) mdf(S,x<<1|1,mid+1,r,tl,tr,d);
}
void get(Seg *S,int x,int l,int r,int nw,int kd){
nw=min(nw,S[x].tag);
if(l==r){
if(kd==0) ans[l]=min(ans[l],nw);
else ans[l]=min(ans[l],l+nw);
return;
}
int mid=l+r>>1;
get(S,x<<1,l,mid,nw,kd);
get(S,x<<1|1,mid+1,r,nw,kd);
}
void dfs(int x){
int p,y;
for(p=go(x);p;p=go[p].nxt){
y=go[p].y;
dfs(y);
if(lef[y]){
if(lef[x]==0) lef[x]=lef[y];
else if(lef[x]>0) lef[x]=-1;
}
}
if(lef[x]>0){//if only one lef
int L=stp[fa[x]]+1;
int R=stp[x];
mdf(seg1,1,1,n,lef[x],lef[x]+L-1, L);
if(L!=R) mdf(seg2,1,1,n,lef[x]+L,lef[x]+R-1, 1-lef[x]);
}
}
int main(){
int i;
scanf("%s",s+1);
n=strlen(s+1);
last=tot=1;
for(i=n;i>0;i--){
last=sam(last,s[i]-'a');
lef[last]=i;
}
for(i=2;i<=tot;i++) go.push(fa[i],i);
dfs(1);
for(i=1;i<=n;i++) ans[i]=INF;
get(seg1,1,1,n,INF,0);
get(seg2,1,1,n,INF,1);
for(i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
bzoj 1396 识别子串 后缀树+线段树的更多相关文章
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...
- BZOJ 1396 识别子串 (后缀自动机+线段树)
题目大意: 给你一个字符串S,求关于每个位置x的识别串T的最短长度,T必须满足覆盖x,且T在S中仅出现一次 神题 以节点x为结尾的识别串,必须满足它在$parent$树的子树中只有一个$endpos$ ...
- bzoj 1396: 识别子串【SAM+线段树】
建个SAM,符合要求的串显然是|right|==1的节点多代表的串,设si[i]为right集合大小,p[i]为right最大的r点,这些都可以建出SAM后再parent树上求得 然后对弈si[i]= ...
- BZOJ1396: 识别子串(后缀自动机 线段树)
题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...
- BZOJ 1396&&2865 识别子串[后缀自动机 线段树]
Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如, ...
- BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)
题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...
- BZOJ 1396 识别子串 (后缀自动机、线段树)
手动博客搬家: 本文发表于20181221 00:58:26, 原地址https://blog.csdn.net/suncongbo/article/details/85150962 嗯,以后博客内容 ...
- 【BZOJ1396】识别子串 - 后缀自动机+线段树
题意: Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. 题解: ...
- bzoj1396&&2865 识别子串 后缀自动机+线段树
Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample Input agoodco ...
随机推荐
- [未完] term.js 记录遇到的问题
参考博文:https://www.cnblogs.com/zhenfei-jiang/p/7065038.html 按照网上查找的资料敲了代码 term.on('data', function(dat ...
- 第2 章Python 语言基础
必背必记 1.转义字符 Python 中的字符串还支持转义字符.所谓转义字符是指使用反斜杠“\”对一些特殊字符进行转义. \ 续行符 \n 换行符 \0 空 \t 水平制表符,用于横向跳到下一制表 ...
- Freemaker基于word模板动态导出压缩文件汇总整理
Freemaker基于word模板动态导出压缩文件汇总整理 Freemaker基于word模板动态导出单个文件思路和代码详情见连接: https://www.cnblogs.com/lsy-blogs ...
- 100个经典C语言程序(益智类)
100个经典C语言程序(益智类) [1.绘制余弦曲线] 在屏幕上用“*”显示0~360度的余弦函数cos(x)曲线 [问题分析与算法设计] 利用cos(x)的左右对称性,将屏幕的行方向定义为x,列方向 ...
- Linux命令之----tree
命令简介 tree命令的中文意思为“树”,功能是以树形结构列出指定目录下的所有内容,包括所有文件.子目录及子目录里的目录和文件. 命令格式 tree [option] [directory]tree ...
- springMVC中接收数组参数
方式一. 后台:public ResultBean queryItemRulesByItemIds(int userId, int[] itemIds) 方式二.
- Java面试——多线程面试题总结
)两者都在等待对方所持有但是双方都不释放的锁,这时便会一直阻塞形成死锁. //存放两个资源等待被使用 public class Resource { public static Object obj1 ...
- LOJ #6008. 「网络流 24 题」餐巾计划
#6008. 「网络流 24 题」餐巾计划 题目描述 一个餐厅在相继的 n nn 天里,每天需用的餐巾数不尽相同.假设第 i ii 天需要 ri r_iri 块餐巾.餐厅可以购买新的餐巾,每块餐 ...
- js 监听后退事件及跳转页面
//直接跳转 window.location.href="b.html"; //返回上一级页面 window.history.back(-1); //返回下一级页面 window. ...
- JS空数组的判断
前言 最近在做一个mini项目,被大神各种鄙视,基础知识确实是不扎实,加油加油.好了,不多废话,抽空写写遇到的两个知识点,就记录下来,写博客还是能帮忙整理记录的,不然过了就忘记了. input监听值改 ...