BZOJ4556 HEOI2016 字符串

10 篇文章 0 订阅
8 篇文章 0 订阅

Problem

BZOJ

Solution

我周围的聚聚一看就说这不是道SA的傻逼题吗……

好吧,考虑用SAM怎么做。
先对反串建出SAM,[a,b]中的所有子串不好处理,因为在parent数上代表的链实在太多了,所以考虑从子串[c,d]开始下手
我们可以考虑二分lcp的长度,那么我们可以在 O ( log ⁡ n ) O(\log n) O(logn)的时间内找到这个子串所对应的节点,然后我们可以用主席树来查在[a,b]中是否有存在这么一个子串。

如何在 O ( log ⁡ n ) O(\log n) O(logn)的时间内找到这个子串所对应的节点?我们在建SAM时可以记录前缀 [ 1 , i ] [1,i] [1,i]所代表的节点,然后在parent树找到最深的节点使得longest大于等于子串长,倍增即可。

时间复杂度 O ( m log ⁡ 2 n ) O(m\log^2 n) O(mlog2n)

Code

#include <assert.h>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=200010,maxm=4000010;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
int n,m,sz,lst,cnt,ans,ch[maxn][26],l[maxn],pre[maxn],pos[maxn];
int t[maxn],top[maxn],f[18][maxn],rt[maxn],lc[maxm],rc[maxm];
char s[maxn];
void insert(int c)
{
	int p=lst,np=++sz;
	l[np]=l[p]+1;lst=np;
	for(;!ch[p][c]&&p;p=pre[p]) ch[p][c]=np;
	if(!p) pre[np]=1;
	else
	{
		int q=ch[p][c];
		if(l[q]==l[p]+1) pre[np]=q;
		else
		{
			int nq=++sz;l[nq]=l[p]+1;
			memmove(ch[nq],ch[q],sizeof(ch[q]));
			pre[nq]=pre[q];pre[np]=pre[q]=nq;
			for(;ch[p][c]==q;p=pre[p]) ch[p][c]=nq;
		}
	}
}
int getpos(int ql,int qr)
{
	int len=qr-ql+1,x=pos[qr];
	for(int i=17;~i;i--)
	  if(l[f[i][x]]>=len)
	    x=f[i][x];
	return x;
}
inline int new_node(){if(cnt+10<maxm) return ++cnt;assert(0);}
void update(int l,int r,int pos,int& rt)
{
	if(!rt) rt=new_node();
	if(l==r) return ;
	int m=(l+r)>>1;
	if(pos<=m) update(l,m,pos,lc[rt]);
	else update(m+1,r,pos,rc[rt]);
}
int query(int l,int r,int L,int R,int rt)
{
	if(!rt) return 0;
	if(L<=l&&r<=R) return 1;
	int m=(l+r)>>1,res=0;
	if(L<=m) res|=query(l,m,L,R,lc[rt]);
	if(res) return 1;
	if(m<R) res|=query(m+1,r,L,R,rc[rt]);
	return res;
}
int merge(int x,int y)
{
	if(!x||!y) return x+y;
	int now=new_node();
	lc[now]=merge(lc[x],lc[y]);
	rc[now]=merge(rc[x],rc[y]);
	return now;
}
void input()
{
	read(n);read(m);
	scanf("%s",s+1);
	reverse(s+1,s+n+1);
	lst=sz=1;
	for(rg int i=1;i<=n;i++)
	{
		insert(s[i]-'a');pos[i]=lst;
		update(1,n,i,rt[lst]);
	}
	for(rg int i=1;i<=sz;i++) ++t[l[i]],f[0][i]=pre[i];
	for(rg int i=1;i<=sz;i++) t[i]+=t[i-1];
	for(rg int i=1;i<=sz;i++) top[t[l[i]]--]=i;
	for(rg int i=1;i<=sz;i++)
	{
		int x=top[i];
		for(rg int j=1;j<18;j++)
		  f[j][x]=f[j-1][f[j-1][x]];
	}
	for(rg int i=sz;i>1;i--)
	{
		int x=top[i];
		rt[pre[x]]=merge(rt[pre[x]],rt[x]);
	}
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	int a,b,c,d,x,L,R,mid;
	input();
	while(m--)
	{
		read(a);read(b);read(c);read(d);
		a=n-a+1;b=n-b+1;c=n-c+1;d=n-d+1;
		L=1;R=c-d+1;ans=0;
		while(L<=R)
		{
			mid=(L+R)>>1;
			x=getpos(c-mid+1,c);
			if(b+mid-1<=a&&query(1,n,b+mid-1,a,rt[x])) ans=mid,L=mid+1;
			else R=mid-1;
		}
		printf("%d\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值