传送门

卡空间差评!

题意简述:给一个字串,对于每个位置求出经过这个位置且只在字串中出现一次的子串的长度的最小值。


解法:先建出samsamsam,显然只有当sizep=1size_p=1sizep​=1的时候才对答案有贡献。

于是对于每个sizep=1size_p=1sizep​=1的状态分情况更新答案。

  1. pos=[pos[p]−len[link[p]]+1,pos[p]]pos=[pos[p]-len[link[p]]+1,pos[p]]pos=[pos[p]−len[link[p]]+1,pos[p]],那么ansi=min{ansi,lenlinkp+1}ans_i=min\{ans_i,len_{link_p}+1\}ansi​=min{ansi​,lenlinkp​​+1}
  2. pos=[pos[p]−len[p]+1,pos[p]−len[link[p]]]pos=[pos[p]-len[p]+1,pos[p]-len[link[p]]]pos=[pos[p]−len[p]+1,pos[p]−len[link[p]]],那么ansi=min{ansi,lenp−i+1}ans_i=min\{ans_i,len_p-i+1\}ansi​=min{ansi​,lenp​−i+1}

第一类直接上线段树。

第二类?我们令fi=ansi+if_i=ans_i+ifi​=ansi​+i,用线段树维护fif_ifi​的值最后统计答案的时候减去iii即可。

代码:

#include<bits/stdc++.h>
#define ri register int
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=9e5+5;
int n,ans[500005];
char s[N];
struct SGT{
	int mn[N<<1];
	inline void build(int p,int l,int r,int f){
		mn[p]=0x3f3f3f3f;
		if(l==r){mn[p]=f?n+l:n;return;}
		build(lc,l,mid,f),build(rc,mid+1,r,f);
	}
	inline void update(int p,int l,int r,int ql,int qr,int v){
		if(ql>r||qr<l)return;
		if(ql<=l&&r<=qr){mn[p]=min(mn[p],v);return;}
		if(qr<=mid)update(lc,l,mid,ql,qr,v);
		else if(ql>mid)update(rc,mid+1,r,ql,qr,v);
		else update(lc,l,mid,ql,mid,v),update(rc,mid+1,r,mid+1,qr,v);
	}
	inline void query(int p,int l,int r,int f){
		if(l==r){ans[l]=min(ans[l],mn[p]-f*l);return;}
		mn[lc]=min(mn[lc],mn[p]),query(lc,l,mid,f);
		mn[rc]=min(mn[rc],mn[p]),query(rc,mid+1,r,f);
	}
}T1,T2;
struct SAM{
	int last,tot,len[N],link[N],son[N][26],siz[N],rk[N],cnt[500005],pos[N];
	SAM(){last=tot=1,len[0]=-1;}
	inline void expand(int x,int id){
		int p=last,np=++tot;
		siz[last=np]=1,pos[np]=id,len[np]=len[p]+1;
		while(p&&!son[p][x])son[p][x]=np,p=link[p];
		if(!p){link[np]=1;return;}
		int q=son[p][x],nq;
		if(len[q]==len[p]+1){link[np]=q;return;}
		len[nq=++tot]=len[p]+1,memcpy(son[nq],son[q],sizeof(son[q])),link[nq]=link[q],link[q]=link[np]=nq;
		while(p&&son[p][x]==q)son[p][x]=nq,p=link[p];
	}
	inline void solve(){
		for(ri i=1;i<=tot;++i)++cnt[len[i]];
		for(ri i=1;i<=n;++i)cnt[i]+=cnt[i-1];
		for(ri i=1;i<=tot;++i)rk[cnt[len[i]]--]=i;
		T1.build(1,1,n,0),T2.build(1,1,n,1);
		for(ri i=tot,p;i;--i){
			p=rk[i];
			if(siz[p]==1){
				T1.update(1,1,n,pos[p]-len[link[p]]+1,pos[p],len[link[p]]+1);
				T2.update(1,1,n,pos[p]-len[p]+1,pos[p]-len[link[p]],len[p]+1);
			}
			siz[link[p]]+=siz[p],pos[link[p]]=max(pos[link[p]],pos[p]);
		}
		fill(ans+1,ans+n+1,n+1);
		T1.query(1,1,n,0),T2.query(1,1,n,1);
		for(ri i=1;i<=n;++i)cout<<ans[i]<<'\n';
	}
}sam;
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	for(ri i=1;i<=n;++i)sam.expand(s[i]-'a',i);
	sam.solve();
	return 0;
}

2018.12.23 bzoj2865&&1396: 字符串识别(后缀自动机+线段树)的更多相关文章

  1. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

  2. BZOJ 2865 字符串识别 | 后缀数组 线段树

    集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...

  3. 【BZOJ-1396&2865】识别子串&字符串识别 后缀自动机/后缀树组 + 线段树

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

  4. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  5. BZOJ1396: 识别子串(后缀自动机 线段树)

    题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...

  6. 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)

    题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...

  7. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  8. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  9. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

随机推荐

  1. 137. Single Number II (Bit)

    Given an array of integers, every element appears three times except for one. Find that single one. ...

  2. LinearLayout 线性布局

    android:orientation 设置布局管理器内组件的排列方式,可设置为 horizontal (水平排列).vertical (垂直排列) android:gravity 设置布局管理器内组 ...

  3. JSON Extractor

    JMeter处理responses 的json 对于请求1返回的结果,处理以后作为请求2的参数,JMeter提供了JSON 提取器 比如 responses 返回: {"statusCode ...

  4. gdb打印C++容器

    将以下内容保存成 .gdbinit 文件放到你的根目录,或者在gdb中source这个文件可以加载. 直接print容器即可. # # STL GDB evaluators/views/utiliti ...

  5. 8.16 val()和html()的问题

    今天在做关闭模态框重置表单时,关闭模态框后输入框里的值还是在,不知道怎么回事? 感谢wd啦,原来我在初始化这个输入框的时候就写错了,输入框写值的时候用的是val(),而我和上面的div一样,用的是ht ...

  6. js学习(初)

    一种弱数据类型语言  var 基础: 处理字符串的函数 数组基础操作 流程控制语句 选择,分支 循环for for in for(索引变量 in  对象){ 语句块 } 面向对象: js语言的对象就是 ...

  7. SI和DI

    si和di是8086CPU中和bx功能相近的寄存器,si和di不能够分成两个8位寄存器来使用. 用si和di实现将字符串"welcome to masm!"复制到它后面的数据区中. ...

  8. jsplumb踩坑

    一,,关于连线器label 我们全局设置中可以用 getInstance 实例化新对象 也可以通过 importDefaults 实例化新对象 当我想要给连线器上添加标签  每个连线器上的标签都不一样 ...

  9. maven library has broken path和pom jar包导入失败

    今天在打开项目的时候,在pom文件添加新的依赖文件发现很多jar都标红,显示不存在,自己查了一上午各种尝试,总结了以下的解决方法. 首先你打开File-Project Structrue-Module ...

  10. Aspose.words四 bookmark

    通过添加bookmark书签来添加数据,首先通过方法MoverToBookmark移动指定的标签位置,然后添加数据,添加完成后清除掉bookmark标示. string templateFile = ...