洛谷 P4595 [COCI2011-2012#5] POPLOCAVANJE(SAM)

这篇博客讲述了在解决洛谷P4595题目的过程中,遇到的空间限制问题以及针对此问题的优化策略。作者探讨了两种暴力解法——5000*5000的AC自动机和带线段树合并的SAM,并指出它们因空间问题而无法适用。随后,作者选择了暴力SAM作为优化方案,详细解释了如何利用SAM对长串建模,匹配短串并进行 dfs 推广最长覆盖位置。然而,即使使用这种方法,当后缀自动机达到6e5规模时,依然可能面临内存错误。
摘要由CSDN通过智能技术生成

题目链接
emmm,为什么最近天天碰到卡这种那种的题目啊,真的自闭了……
首先吧,这题最暴力的两种做法
建5000*5000的ac自动机
建带线段树合并的SAM
思路都比较简单,而且如果不卡空间的话复杂度都挺对
但可惜它卡空间……
这个时候对应的有两种优化方法
分块定期重构的ac自动机
暴力SAM……
因为SAM比较好想就写了SAM
思路是先对长串建SAM,把每个短串都在SAM里匹配一遍,如果能完全匹配说明这个串能用
在这个串对应的节点上插个长度,接着dfs的时候往下推mx,推到新加进来的np节点,这个时候np节点的mx就是以这个点为结尾最长被覆盖的位置,差分一下就可以了
但其实如果他把后缀自动机卡满也就是开到6e5的话,肯定还是会MLE的……

代码如下:

#include<bits/stdc++.h>
using namespace std;

struct node
{
	int len,fa,son[26];
}t[500050];

int head[500050],nxt[500050],to[500050];
int last=1,cnt=1,mx[500050],pos[300010],vis[300010];
int ans,tot;
char s[300010];

void add(int c)
{
	int p=last;
	int np=++cnt;
	t[np].len=t[p].len+1;
	while(p&&(!t[p].son[c]))
	{
		t[p].son[c]=np;
		p=t[p].fa;
	}
	if(!p) t[np].fa=1;
	else
	{
		int q=t[p].son[c],nq;
		if(t[p].len+1==t[q].len)
		{
			t[np].fa=q;
		}
		else
		{
			nq=++cnt;
			t[nq]=t[q];
			t[nq].len=t[p].len+1;
			t[np].fa=t[q].fa=nq;
			while(p&&t[p].son[c]==q)
			{
				t[p].son[c]=nq;
				p=t[p].fa;
			}
		}
	}
	last=np;
}


void dfs(int now)
{
	mx[now]=max(mx[t[now].fa],mx[now]);
	for(int i=head[now];i;i=nxt[i])
	{
		dfs(to[i]);
	}
}

int n,m;

int main()
{
	scanf("%d",&n);
	scanf("%s",s);
	int lena=strlen(s);
	int len=lena;
	for(int i=0;i<lena;i++) add(s[i]-'a'),pos[i]=last;
	scanf("%d",&m);
	for(int ttt=1;ttt<=m;ttt++)
	{
		scanf("%s",s);
		lena=strlen(s);
		int rt=1;
		for(int i=0;i<lena;i++)
		{
			if(!t[rt].son[s[i]-'a'])
			{
				rt=-1;
				break;
			}
			else rt=t[rt].son[s[i]-'a'];
		}
		if(~rt)
		{
			mx[rt]=max(mx[rt],lena);
		}
	}
	for(int i=1;i<=cnt;i++)
	{
		nxt[++tot]=head[t[i].fa];
		to[tot]=i;
		head[t[i].fa]=tot;
	}
	dfs(1);
	for(int i=0;i<len;i++) vis[i-mx[pos[i]]+1]++,vis[i+1]--;
	for(int i=1;i<len;i++) vis[i]=vis[i-1]+vis[i];
	for(int i=0;i<len;i++) ans+=(!vis[i]);
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值