BZOJ4516 [Sdoi2016]生成魔咒

题意:

一个初始为空的串,每次在末尾插入一个字符,每次插入后问字符串中本质不同的子串的个数。字符值域1e9。

知识点:

后缀自动机

解法:

因为SAM本来就是支持动态末尾插入的在线结构,而且SAM有一个性质是本质不同子串个数等于所有点的len减去parent树上的父亲点的len,所以每次插入改ans即可。

备注:

因为值域很大,所以改用map维护转移的字符。
另外好像后缀数组也可以做,加点什么平衡树也可以做,不过我这种方法应该代码最好写、最短(里面有一些map的find、insert操作可以在访问空节点的时候变快)。

#include<cstdio>
#include<cstring>
#include<map>
using namespace std;

typedef long long ll;
const int maxn=200010;
ll ans;
int n,m,tot,lst;
struct sam
{
	map<int,int>son;
	int fa,len;
}a[maxn];

int read()
{
	int x=0;
	char c=getchar();
	while (c<48||c>57)
		c=getchar();
	while (c>=48&&c<=57)
		x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

void sam_insert(int c)
{
	int p=lst,u=(++tot);
	lst=u;
	a[u].len=a[p].len+1;
	for (;p&&a[p].son.find(c)==a[p].son.end();p=a[p].fa)
		a[p].son.insert(make_pair(c,u));
	if (!p)
		a[u].fa=1;
	else
	{
		int v=a[p].son[c];
		if (a[v].len==a[p].len+1)
			a[u].fa=v;
		else
		{
			int w=(++tot);
			a[w]=a[v];
			a[w].len=a[p].len+1;
			a[v].fa=a[u].fa=w;
			for (;p&&a[p].son[c]==v;p=a[p].fa)
				a[p].son[c]=w;
		}
	}
	ans+=a[u].len-a[a[u].fa].len;
}

int main()
{
	int x;
	tot=lst=1;
	n=read();
	while (n--)
	{
		x=read();
		sam_insert(x);
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2020-02-11 11:44  MN2016  阅读(101)  评论(0编辑  收藏  举报