码迷,mamicode.com
首页 > 其他好文 > 详细

【bzoj5084】hashit 广义后缀自动机+树链的并+STL-set

时间:2018-04-03 21:55:37      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:使用   节点   时间   pos   algo   strlen   amp   family   set   

题目描述

你有一个字符串S,一开始为空串,要求支持两种操作
在S后面加入字母C
删除S最后一个字母
问每次操作后S有多少个两两不同的连续子串

输入

一行一个字符串Q,表示对S的操作
如果第i个字母是小写字母c,表示第一种加字母c的操作
如果为-表示删除操作,保证所有删除操作前S都非空
|Q|<=10^5

输出

输出|Q|行,第i行表示i个操作之后S内有多少个不同子串

样例输入

aba-caba

样例输出

1
3



9
12
17


题解

广义后缀自动机+树链的并+STL-set

题目给出的字符串是一棵Trie的形式,我们对其建出广义后缀自动机。

那么每次我们要求的就是:Trie树上当前所有点在后缀自动机pre树到根节点的路径所覆盖的所有点的 $dis[pre[i]]-dis[i]$ 之和,即树链的并的长度。

我们使用STL-set维护动态插入删除节点的树链的并即可。

时间复杂度 $O(26n+n\log n)$ 。

第一次写正常的广义SAM = =

#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
using namespace std;
set<int> s;
set<int>::iterator it;
char str[N];
int tc[N][26] , tf[N] , tt = 1 , pos[N] , c[N][26] , pre[N] , dis[N] , tot = 1 , head[N] , to[N] , next[N] , cnt , fa[N][20] , deep[N] , log[N] , vp[N] , rp[N] , tp;
inline int insert(int x , int p)
{
	if(c[p][x])
	{
		int q = c[p][x];
		if(dis[q] == dis[p] + 1) return q;
		else
		{
			int nq = ++tot;
			memcpy(c[nq] , c[q] , sizeof(c[q]));
			dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , pre[q] = nq;
			while(p && c[p][x] == q) c[p][x] = nq , p = pre[p];
			return nq;
		}
	}
	else
	{
		int np = ++tot;
		dis[np] = dis[p] + 1;
		while(p && !c[p][x]) c[p][x] = np , p = pre[p];
		if(!p) pre[np] = 1;
		else
		{
			int q = c[p][x];
			if(dis[q] == dis[p] + 1) pre[np] = q;
			else
			{
				int nq = ++tot;
				memcpy(c[nq] , c[q] , sizeof(c[q]));
				dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , pre[np] = pre[q] = nq;
				while(p && c[p][x] == q) c[p][x] = nq , p = pre[p];
			}
		}
		return np;
	}
}
void build(int x)
{
	int i;
	for(i = 0 ; i < 26 ; i ++ )
		if(tc[x][i])
			pos[tc[x][i]] = insert(i , pos[x]) , build(tc[x][i]);
}
inline void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
	int i;
	vp[x] = ++tp , rp[tp] = x;
	for(i = 1 ; i <= log[deep[x]] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(i = head[x] ; i ; i = next[i]) fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
}
inline int lca(int x , int y)
{
	int i;
	if(deep[x] < deep[y]) swap(x , y);
	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
		if(deep[x] - deep[y] >= (1 << i))
			x = fa[x][i];
	if(x == y) return x;
	for(i = log[deep[x]] ; ~i ; i -- )
		if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
			x = fa[x][i] , y = fa[y][i];
	return fa[x][0];
}
int main()
{
	int q , i , now = 1 , x , y;
	long long ans = 0;
	scanf("%s" , str) , q = strlen(str);
	for(i = 0 ; i < q ; i ++ )
	{
		if(str[i] == ‘-‘) now = tf[now];
		else
		{
			if(!tc[now][str[i] - ‘a‘]) tc[now][str[i] - ‘a‘] = ++tt , tf[tt] = now;
			now = tc[now][str[i] - ‘a‘];
		}
	}
	pos[1] = 1 , build(1);
	for(i = 2 ; i <= tot ; i ++ ) add(pre[i] , i) , log[i] = log[i >> 1] + 1;
	dfs(1);
	now = 1;
	for(i = 0 ; i < q ; i ++ )
	{
		if(str[i] == ‘-‘)
		{
			ans -= dis[pos[now]] , s.erase(vp[pos[now]]);
			x = y = 0 , it = s.upper_bound(vp[pos[now]]);
			if(it != s.end()) x = rp[*it];
			if(it != s.begin()) y = rp[*--it];
			if(x) ans += dis[lca(pos[now] , x)];
			if(y) ans += dis[lca(pos[now] , y)];
			if(x && y) ans -= dis[lca(x , y)];
			now = tf[now];
		}
		else
		{
			now = tc[now][str[i] - ‘a‘] , ans += dis[pos[now]];
			x = y = 0 , it = s.upper_bound(vp[pos[now]]);
			if(it != s.end()) x = rp[*it];
			if(it != s.begin()) y = rp[*--it];
			if(x) ans -= dis[lca(pos[now] , x)];
			if(y) ans -= dis[lca(pos[now] , y)];
			if(x && y) ans += dis[lca(x , y)];
			s.insert(vp[pos[now]]);
		}
		printf("%lld\n" , ans);
	}
	return 0;
}

 

 

【bzoj5084】hashit 广义后缀自动机+树链的并+STL-set

标签:使用   节点   时间   pos   algo   strlen   amp   family   set   

原文地址:https://www.cnblogs.com/GXZlegend/p/8711038.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!