BZOJ4516 [Sdoi2016]生成魔咒 后缀自动机/后缀数组

题意:一个串初始为空,n次像串尾添加元素,每次添加后回答本质不同的子串个数

n<=1e5 字符集大小1e9

Sol:

本质不同的子串个数,考虑后缀数据结构

发现向结尾添加字符对后缀数组不友好,但是在开头添加很资瓷,相当于新添加一个后缀

于是考虑离线,把最终串搞出来在翻转一下,建立后缀数组,问题转化成动态添加后缀求不同子串个数

每次找到当前后缀应在位置,去掉后缀对前驱的影响,加上后继与前驱对她的影响,递推即可

找后缀可以用set

也可以再将这个过程逆转,每次删除后缀,这样可以用双线链表找前驱后继

算影响用正常的RMQ算法

因为字符集大需要先离散化

总复杂度O(nlogn)

好麻烦啊。。。年轻人,你听说过后缀自动机吗

会后缀自动机后发现这是一道裸题,字符集大?map一发

总复杂度也是O(nlogn) 还是在线的,比后缀数组高到不知道哪里去了

科技是第一生产力啊2333

Code:

后缀数组

//Suffix_Array - 528ms
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100009, Log = 18;

int c[maxn],t[maxn],t2[maxn],sa[maxn],rank[maxn],height[maxn];
int s[maxn],b[maxn],ft[maxn],nxt[maxn],pre[maxn];
int n;
long long ans[maxn];
struct ST
{
	int mn[Log+1][maxn];
	void build(int *a)
	{
		for(int i=1;i<=n;i++) mn[0][i]=a[i];
		for(int i=1;i<=Log;i++)
			for(int j=1;j<=n-(1<<i)+1;j++)
				mn[i][j]=min(mn[i-1][j],mn[i-1][j+(1<<i>>1)]);
	}
	int query(int l,int r)
	{
		int len=ft[r-l+1];
		return min(mn[len][l],mn[len][r-(1<<len)+1]);
	}
}H;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int find(int x)
{
	int l=1,r=b[0];
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(b[mid]==x) return mid;
		if(b[mid]<x) l=mid+1;else r=mid-1;
	}
}

void build()
{
	int mr=b[0],*x=t,*y=t2;
	for(int i=1;i<=mr;i++) c[i]=0;
	for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
	for(int i=1;i<=mr;i++) c[i]+=c[i-1];
	for(int i=n;i>=1;i--) sa[c[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1)
	{
		int p=0;
		for(int i=n-k+1;i<=n;i++) y[++p]=i;
		for(int i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k;
		for(int i=1;i<=mr;i++) c[i]=0;
		for(int i=1;i<=n;i++) c[x[y[i]]]++;
		for(int i=1;i<=mr;i++) c[i]+=c[i-1];
		for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i];
		swap(x,y);
		p=1;x[sa[1]]=1;
		for(int i=2;i<=n;i++)
			if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]) x[sa[i]]=p;
			else x[sa[i]]=++p;
		if(p>=n) break;
		mr=p;
	}
	for(int i=1;i<=n;i++) rank[sa[i]]=i;
	int h=0;
	for(int i=1;i<=n;i++)
	{
		if(rank[i]==1) h=0;
		else
		{
			int k=sa[rank[i]-1];
			h--;if(h<0) h=0;
			while(s[k+h]==s[i+h]) h++;
		}
		height[rank[i]]=h;
	}
	H.build(height);
	for(int i=1;(1<<i)<=n;i++) ft[1<<i]=i;
	for(int i=1;i<=n;i++) if(!ft[i]) ft[i]=ft[i-1];
	for(int i=1;i<=n;i++) nxt[i]=i+1,pre[i]=i-1;
	nxt[n]=pre[1]=0;
	
}

int lcp(int x,int y){return H.query(x+1,y);}

void erase(int x)
{
	nxt[pre[x]]=nxt[x];
	pre[nxt[x]]=pre[x];
	nxt[x]=pre[x]=0;
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++) b[i]=s[i]=read();
	sort(b+1,b+1+n);b[0]=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++) s[i]=find(s[i]);
	reverse(s+1,s+1+n);
	build();
	for(int i=2;i<=n;i++) ans[1]-=height[i];
	for(int i=2;i<=n;i++)
	{
		ans[i]=ans[i-1];
		int x=rank[i-1],l=pre[x],r=nxt[x];
		if(l) ans[i]+=lcp(l,x);
		if(r) ans[i]+=lcp(x,r);
		if(l&&r) ans[i]-=lcp(l,r);
		erase(x);
	}
	for(int i=n;i>=1;i--) ans[n-i+1]+=1ll*(i+1)*i/2;
	for(int i=n;i>=1;i--) printf("%lld\n",ans[i]);
	return 0;
}
后缀自动机
//Suffix_Automaton - 764ms
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100009;

struct SAM
{
	int tot,last,root;
	long long ans;
	struct state
	{
		map<int,int>son;
		int mx,par;
	}node[maxn<<1];
	int calc(int x)
	{
		if(x==root) return 0;
		return node[x].mx-node[node[x].par].mx;
	}
	void init(){tot=last=root=1;}
	void extend(int x)
	{
		int p=last,np=++tot;
		node[np].mx=node[p].mx+1;
		while(p&&node[p].son[x]==0)
			node[p].son[x]=np,p=node[p].par;
		if(p==0)
		{
			node[np].par=root;
			ans+=calc(np);
		}
		else
		{
			int q=node[p].son[x];
			if(node[p].mx+1==node[q].mx)
			{
				node[np].par=q;
				ans+=calc(np);
			}
			else
			{
				int nq=++tot;
				node[nq].son=node[q].son;
				node[nq].mx=node[p].mx+1;
				ans-=calc(q);
				node[nq].par=node[q].par;
				node[np].par=node[q].par=nq;
				ans+=calc(np)+calc(nq)+calc(q);
				while(p&&node[p].son[x]==q)
					node[p].son[x]=nq,p=node[p].par;
			}
		}
		last=np;
	}
}uuz;
int n;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

int main()
{
	n=read();uuz.init();
	for(int i=1;i<=n;i++)
	{
		int x=read();
		uuz.extend(x);
		printf("%lld\n",uuz.ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值