bzoj1396 识别子串

题面
https://www.lydsy.com/JudgeOnline/problem.php?id=1396
首先,对于题目所说的T在S中只出现一次这个要求,
可以利用后缀自动机的parent树的叶子节点的性质来满足要求。
显然所有满足T在S中只出现过一次的子串就是parent树所有叶子节点的right集合的并集。
考虑怎么去找到最小值。
对于一个叶子节点来说,其right集合中所有串的右端点显然是一样的,左端点依次向右,长度依次递减,直到左端点不再属于这个点的right集合位置。
可以用一个式子来描述rifht集合的左端点的范围【id[x]-s[x].len+1,id[x]-s[s[x].link].len】。
进一步地发现这个right集合对答案的贡献是一条斜线和一条水平线(这里所说的贡献是使答案对其取min)
因为对于【id[x]-s[x].len+1,id[x]-s[s[x].link].len】可以产生的贡献随着长度依次递减,是一条斜线。
而对于【id[x]-s[s[x].link].len+1,id[x]】,由于左端点位于这个位置时,不再满足“T在S中只出现过一次”这一要求。
因此可以产生的贡献只能是刚才那条斜线的右端点的水平延长线。
分析完操作后,考虑怎么实现这两个区间操作。
有一个自闭的想法是直接强行上一个李超线段树就完事了,然而并没有必要。
发现由于只关心最后的答案,可以离线,即把斜线和水平线分开做,跑两次,分别用不同的线段树维护,这样就simple了很多。

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#define N 330000
#define L 300000
#define eps 1e-7
#define inf 1e9+7
#define ll long long
using namespace std;
inline int read()
{
	char ch=0;
	int x=0,flag=1;
	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*flag;
}
char ch[N];
bool flag[N];
int root=1,size=1,last=1,id[N];
struct node{int len,link,nxt[26];}s[N];
void insert(int k)
{
	int cur=++size,p=last;
	s[cur].len=s[p].len+1;
	while(p&&!s[p].nxt[k])
	{
		s[p].nxt[k]=cur;
		p=s[p].link;
	}
	last=cur;
	if(!p){s[cur].link=root;return;}
	int q=s[p].nxt[k];
	if(s[q].len==s[p].len+1)s[cur].link=q;
	else
	{
		int clone=++size;
		s[clone]=s[q];
		s[clone].len=s[p].len+1;
		while(p&&s[p].nxt[k]==q)
		{
			s[p].nxt[k]=clone;
			p=s[p].link;
		}
		s[q].link=s[cur].link=clone;
	}
}
struct Segment_Tree
{
	#define lson o<<1
	#define rson o<<1|1
	#define mid ((l+r)>>1)
	int num,setv[N*4];
	inline void pushdown(int o,int l,int r)
	{
		if(setv[o]==inf)return;
		setv[lson]=min(setv[lson],setv[o]);
		setv[rson]=min(setv[rson],setv[o]-(mid-l+1));
		setv[o]=inf;
	}
	void build(int o,int l,int r)
	{
		setv[o]=inf;
		if(l==r)return;
		build(lson,l,mid);
		build(rson,mid+1,r);
	}
	void optset(int o,int l,int r,int ql,int qr)
	{
		if(ql<=l&&r<=qr)
		{
			setv[o]=min(setv[o],num);
			num-=r-l+1;
			return;
		}
		pushdown(o,l,r);
		if(ql<=mid)optset(lson,l,mid,ql,qr);
		if(qr>mid)optset(rson,mid+1,r,ql,qr);
		return;
	}
	void rebuild(int o,int l,int r)
	{
		if(l==r)return;
		pushdown(o,l,r);
		rebuild(lson,l,mid);
		rebuild(rson,mid+1,r);
		setv[o]=inf;
	}
	inline void pushdown_(int o)
	{
		if(setv[o]==inf)return;
		setv[lson]=min(setv[lson],setv[o]);
		setv[rson]=min(setv[rson],setv[o]);
		setv[o]=inf;
	}
	void optset_(int o,int l,int r,int ql,int qr)
	{
		if(ql<=l&&r<=qr)
		{
			setv[o]=min(setv[o],num);
			return;
		}
		pushdown_(o);
		if(ql<=mid)optset_(lson,l,mid,ql,qr);
		if(qr>mid)optset_(rson,mid+1,r,ql,qr);
		return;
	}
	void print(int o,int l,int r)
	{
		if(l==r)
		{
			printf("%d\n",setv[o]);
			return;
		}
		pushdown_(o);
		print(lson,l,mid);
		print(rson,mid+1,r);
	}
}T;
int main()
{
	int n;
	scanf("%s",ch);n=strlen(ch);
	for(int i=0;i<n;i++)insert(ch[i]-'a'),id[last]=i;
	T.build(1,0,n-1);
	for(int x=1;x<=size;x++)flag[s[x].link]=true;
	for(int x=1;x<=size;x++)if(!flag[x])
	{
		int l=id[x]-s[x].len+1;
		int r=id[x]-s[s[x].link].len;
		T.num=s[x].len;
		T.optset(1,0,n-1,l,r);
	}
	T.rebuild(1,0,n-1);
	for(int x=1;x<=size;x++)if(!flag[x])
	{
		int l=id[x]-s[s[x].link].len+1;
		int r=id[x];
		T.num=s[s[x].link].len+1;
		T.optset_(1,0,n-1,l,r);
	}
	T.print(1,0,n-1);
	return 0;
}
posted @ 2018-12-20 14:14  Creed-qwq  阅读(124)  评论(0编辑  收藏  举报