BZOJ1396: 识别子串(后缀自动机,线段树)
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
解题思路:
思考一个最小识别串会带来什么。
在它内部的点的答案就是这个串的长度,而在它外面的点需要拓展到这个串的端点。
首先,一个Parent树内的节点对应子串是唯一识别的,那么其|Right|必定=1,那么对应一个|Right|=1的节点
因为Parent树上的节点是其子节点的后缀,而Parent树是逆序的,所以一棵|Right|=1的子树内结尾是一定的,设为endpos。
那么在一个|Right|=1的节点内其有效压缩长度为len-len[fa],那么这段子串可以认为是等价唯一的。其长度为其到endpos的距离。
而在len[fa]以下的,识别长度太小必须使用len[fa]为最小识别长度。
区间修改当然要线段树了。
在len[fa]~len区间内,其答案为endpos-pos+1。
在1~len[fa]区间内,其答案为len[fa]
pos是与位置有关的变量最后算。
只需将endpos-1,len[fa]推入线段树好了
代码:
(不要问我为什么上次拓扑这次建边因为我闲的)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lll spc<<1
#define rrr spc<<1|1
struct sant{
int tranc[];
int len;
int pre;
}s[];
struct pnt{
int hd;
int wgt;
int endp;
}p[];
struct ent{
int twd;
int lst;
}e[];
struct trnt{
int minval;
int lzt;
bool al;
};
int cnt;
int siz;
int fin;
int n;
char tmp[];
class Sgmt_tree{
public:
void res(void)
{
for(int i=;i<;i++)
tr[i].lzt=tr[i].minval=0x3f3f3f3f;
return ;
}
void pushup(int spc)
{
tr[spc].minval=std::min(tr[lll].minval,tr[rrr].minval);
return ;
}
void Add(int spc,int x)
{
tr[spc].minval=std::min(tr[spc].minval,x);
tr[spc].lzt=std::min(tr[spc].lzt,x);
tr[spc].al=true;
return ;
}
void pushdown(int spc)
{
if(tr[spc].al)
{
Add(lll,tr[spc].lzt);
Add(rrr,tr[spc].lzt);
tr[spc].al=false;
}
return ;
}
void update(int l,int r,int ll,int rr,int spc,int v)
{
if(l>rr||ll>r)
return ;
if(ll<=l&&r<=rr)
{
Add(spc,v);
return ;
}
int mid=(l+r)>>;
pushdown(spc);
update(l,mid,ll,rr,lll,v);
update(mid+,r,ll,rr,rrr,v);
pushup(spc);
return ;
}
int query(int l,int r,int pos,int spc)
{
if(l==r)
return tr[spc].minval;
int mid=(l+r)>>;
pushdown(spc);
if(pos<=mid)
return query(l,mid,pos,lll);
else
return query(mid+,r,pos,rrr);
}
private:
trnt tr[];
}T[];
void Insert(int c,int w)
{
int nwp,nwq,lsq,lsp;
nwp=++siz;
p[nwp].wgt=;
p[nwp].endp=w;
s[nwp].len=s[fin].len+;
for(lsp=fin;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)
s[lsp].tranc[c]=nwp;
if(!s[lsp].tranc[c])
s[lsp].tranc[c]=nwp;
else{
lsq=s[lsp].tranc[c];
if(s[lsq].len==s[lsp].len+)
s[nwp].pre=lsq;
else{
nwq=++siz;
s[nwq]=s[lsq];
s[nwq].len=s[lsp].len+;
s[nwp].pre=s[lsq].pre=nwq;
while(s[lsp].tranc[c]==lsq)
{
s[lsp].tranc[c]=nwq;
lsp=s[lsp].pre;
}
}
}
fin=nwp;
return ;
}
void ade(int f,int t)
{
cnt++;
e[cnt].twd=t;
e[cnt].lst=p[f].hd;
p[f].hd=cnt;
return ;
}
void Dfs(int x)
{
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
Dfs(to);
p[x].wgt+=p[to].wgt;
p[x].endp=std::min(p[x].endp,p[to].endp);
}
if(p[x].wgt==)
{
int minlen=s[s[x].pre].len+;
int maxlen=s[x].len;
int endpos=p[x].endp;
T[].update(,n,endpos-maxlen+,endpos-minlen+,,endpos+);
T[].update(,n,endpos-minlen+,endpos,,minlen);
}
return ;
}
int main()
{
scanf("%s",tmp+);
n=strlen(tmp+);
for(int i=;i<=n;i++)
Insert(tmp[i]-'a',i);
for(int i=;i<=siz;i++)
ade(s[i].pre,i);
T[].res();
T[].res();
Dfs();
for(int i=;i<=n;i++)
{
int ans1,ans2;
ans1=T[].query(,n,i,)-i;
ans2=T[].query(,n,i,);
printf("%d\n",std::min(ans1,ans2));
}
return ;
}
BZOJ1396: 识别子串(后缀自动机,线段树)的更多相关文章
- BZOJ1396: 识别子串(后缀自动机 线段树)
题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...
- 【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 ...
- BZOJ 1396&&2865 识别子串[后缀自动机 线段树]
Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如, ...
- BZOJ 1396 识别子串 (后缀自动机+线段树)
题目大意: 给你一个字符串S,求关于每个位置x的识别串T的最短长度,T必须满足覆盖x,且T在S中仅出现一次 神题 以节点x为结尾的识别串,必须满足它在$parent$树的子树中只有一个$endpos$ ...
- bzoj 1396/2865: 识别子串 后缀自动机+线段树
水水的字符串题 ~ #include <map> #include <cstdio> #include <cstring> #include <algorit ...
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...
- BZOJ1396 识别子串 字符串 SAM 线段树
原文链接http://www.cnblogs.com/zhouzhendong/p/9004467.html 题目传送门 - BZOJ1396 题意 给定一个字符串$s$,$|s|\leq 10^5$ ...
- 2018.12.23 bzoj2865&&1396: 字符串识别(后缀自动机+线段树)
传送门 卡空间差评! 题意简述:给一个字串,对于每个位置求出经过这个位置且只在字串中出现一次的子串的长度的最小值. 解法:先建出samsamsam,显然只有当sizep=1size_p=1sizep ...
- bzoj1396识别子串(SAM+线段树)
复习SAM板子啦!考前刷水有益身心健康当然这不是板子题/水题…… 很容易发现只在i位置出现的串一定是个前缀串.那么对答案的贡献分成两部分:一部分是len[x]-fa~len[x]的这部分贡献会是r-l ...
随机推荐
- POJ 3181完全背包(+高精)
思路: f[i]表示还剩i空间的方案数 套个高精 (网上有人把它拆成了两个long long) 其实这道题的高精并不难写-.. //By SiriusRen #include <cstdio&g ...
- Python环境搭建—安利Python小白的Python安装详细教程
人生苦短,我用Python.众所周知,Python目前越来越火,学习Python的小伙伴也越来越多.最近看到群里的小伙伴经常碰到不会安装Python或者不知道去哪下载Python安装包等系列问题,为了 ...
- tensorflow学习之路---简单的代码
import numpyimport tensorflow as tf #自己创建的数据x_data = numpy.random.rand(100).astype(numpy.float32)#创建 ...
- Mysql学习总结(8)——MySql基本查询、连接查询、子查询、正则表达查询讲解
查询数据指从数据库中获取所需要的数据.查询数据是数据库操作中最常用,也是最重要的操作.用户可以根据自己对数据的需求,使用不同的查询方式.通过不同的查询方式,可以获得不同的数据.MySQL中是使用SEL ...
- C# async/await异步编程深入理解
异步函数简介 一般指 async 修饰符声明得.可包含await表达式得方法或匿名函数. 声明方式 异步方法的声明语法与其他方法完全一样, 只是需要包含 async 关键字.async可以出现在返回值 ...
- 參考mudo logging写的win下logging
#pragma once #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> #include & ...
- Android Developer:内存分析器
Heap Viewer,Memory Monitor和Allocation Tracker是用来可视化你的app使用内存的补充工具. 使用Memory Monitor Tool来发现是否有不好的内存回 ...
- {head first} --- networking 3
Chapter 10 无线网络 续 .. Chapter 11 网络安全 Chapter 12 设计网络 涉及到物理的设计网络布局等.我没细看. 附录
- jquery19 ajax()
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...
- thinkphp中 Illegal offset type异常
thinkphp中 Illegal offset type异常 一.错误提示 二.解决思路 1.看出错提示中的函数为assign函数,那说明是我们在从控制器assign数据到页面的部分出现了错误 2. ...