BZOJ1396 识别子串 和 BZOJ2865 字符串识别

字符串识别

2865: 字符串识别

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 839  Solved: 261
[Submit][Status][Discuss]

Description

XX在进行字符串研究的时候,遇到了一个十分棘手的问题。

在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件:

1、iKj

2、子串T只在S中出现过一次。

例如,S="banana"K=5,则关于第K位的识别子串有"nana""anan""anana""nan""banan""banana"

现在,给定SXX希望知道对于S的每一位,最短的识别子串长度是多少,请你来帮助他。

Input

仅一行,输入长度为N的字符串S

Output

输出N行,每行一个整数,第i行的整数表示对于第i位的最短识别子串长度。

Sample Input

agoodcookcooksgoodfood

Sample Output


1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

HINT

N<=5*10^5

Source

[Submit][Status][Discuss]

HOME Back

jklover的题解

SAM + 线段树.

先建出 parent 树,按照题意,我们只需要处理 right 集合大小为 1 的节点.
如下图,先算出这样的一个节点合法长度的 max,min ( min 可以用 max(fa)+1 计算).

![](https://jkloverdcoi.github.io/2019/04/27/bzoj-1396-%E8%AF%86%E5%88%AB%E5%AD%90%E4%B8%B2/1.png)
那么区域 I 内每个点的贡献就是区域 II 的长度加上这个点到区域 II 的距离. 区域 II 内每个点的贡献就是区间 II 的长度.开两颗线段树分别修改就可以了.

如何维护区域1呢?考虑他的答案是pos[u]-i+1,直接更新pos[u],那么把最终线段树上的值-i就是答案了。这种操作比较常见。

时间复杂度(O(n log n))

co int N=2e5,INF=0x3f3f3f3f;
struct node{int min,tag;};
#define lc (x<<1)
#define rc (x<<1|1)
struct SegTree{
	node t[N*4];
	void pushup(int x){
		t[x].min=min(t[lc].min,t[rc].min);
	}
	void modify(int x,int v){
		t[x].min=min(t[x].min,v),t[x].tag=min(t[x].tag,v);
	}
	void pushdown(int x){
		if(t[x].tag<INF){
			modify(lc,t[x].tag),modify(rc,t[x].tag);
			t[x].tag=INF;
		}
	}
	void build(int x,int l,int r){
		t[x].min=t[x].tag=INF;
		if(l==r) return;
		int mid=l+r>>1;
		build(lc,l,mid),build(rc,mid+1,r);
	}
	void update(int x,int l,int r,int ql,int qr,int v){
		if(ql<=l&&r<=qr) return modify(x,v);
		pushdown(x);
		int mid=l+r>>1;
		if(ql<=mid) update(lc,l,mid,ql,qr,v);
		if(qr>mid) update(rc,mid+1,r,ql,qr,v);
		pushup(x);
	}
}T1,T2;
int ans[N];
void query(int x,int l,int r){
	if(l==r) return ans[l]=min(T1.t[x].min-l,T2.t[x].min),void();
	T1.pushdown(x),T2.pushdown(x);
	int mid=l+r>>1;
	query(lc,l,mid),query(rc,mid+1,r);
}
// SAM
char buf[N];
int n,tot=1,last=1;
int ch[N][26],fa[N],len[N],pos[N],siz[N];
void extend(int c,int po){
	int p=last,cur=last=++tot;
	len[cur]=len[p]+1,pos[cur]=po,siz[cur]=1;
	for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
	if(!p) fa[cur]=1;
	else{
		int q=ch[p][c];
		if(len[q]==len[p]+1) fa[cur]=q;
		else{
			int clone=++tot;
			memcpy(ch[clone],ch[q],sizeof ch[q]);
			fa[clone]=fa[q],len[clone]=len[p]+1,pos[clone]=pos[q];
			fa[q]=fa[cur]=clone;
			for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
		}
	}
}
int cnt[N],ord[N];
int main(){
	scanf("%s",buf+1),n=strlen(buf+1);
	for(int i=1;i<=n;++i) extend(buf[i]-'a',i);
	for(int i=1;i<=tot;++i) ++cnt[len[i]];
	for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
	for(int i=1;i<=tot;++i) ord[cnt[len[i]]--]=i;
	T1.build(1,1,n),T2.build(1,1,n);
	for(int i=tot;i>=1;--i){
		int u=ord[i];
		siz[fa[u]]+=siz[u];
		if(u==1||siz[u]>1) continue;
		int l=pos[u]-len[u]+1,r=pos[u]-len[fa[u]];
		if(l<=r-1) T1.update(1,1,n,l,r-1,pos[u]+1);
		T2.update(1,1,n,r,pos[u],len[fa[u]]+1);
	}
	query(1,1,n);
	for(int i=1;i<=n;++i) printf("%d
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/10805402.html