BZOJ 2754 SCOI2012 喵星球上的点名 fail树+set启发式合并

11 篇文章 0 订阅
6 篇文章 0 订阅

题目大意:给定n个目标串和m个模式串,问这m个模式串每个在多少个目标串中出现过,以及n个目标串每个以最多多少个模式串为子串

我错了……就算用fail树+set启发式合并也优化不到O(nlog^2n)……这题的数据范围相当无解啊

首先将所有名字和点名的字符串全都插进AC自动机

将每个点上开一个set记录这个点是哪些喵星人的名字的前缀

然后建立fail树 沿着fail树从下到上启发式合并

每合并完一个点 如果这个点是某次点名的字符串 那么这次点名点到的喵星人就是这个点的set中的所有元素 统计答案即可

统计答案以外的操作显然是O(nlog^2n) 但是统计答案这里还是没有避免高复杂度

最终复杂度为O(nlog^2n+Σans2) 其中ans2是第二问的答案

感觉这个算法的瓶颈就在Σans2上了……或许我们可以换一种做法统计ans2?感觉……不是很必要的样子……

#include <set>
#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200200
using namespace std;
struct Trie{
	Trie *fail;
	map<int,Trie*> son;
	set<int> s;
	vector<Trie*> fail_son;
	int ed,cnt;
}*root,mempool[M],*C=mempool;
int n,m,ans1[50500],ans2[20200];
int substitute[50500];
void Insert(Trie* &p,int left,int pos,bool flag)
{
	if(!p) p=C++;
	if(!flag) p->s.insert(pos);
	if(!left)
	{
		if(flag)
		{
			if(p->ed)
				substitute[p->ed]=pos;
			p->ed=pos;
			p->cnt++;
		}
		return ;
	}
	int temp;
	scanf("%d",&temp);
	Insert(p->son[temp],left-1,pos,flag);
}
void Build_Tree()
{
	static Trie *q[M];
	static int r,h;
	map<int,Trie*>::iterator it;
	for(it=root->son.begin();it!=root->son.end();it++)
	{
		it->second->fail=root;
		root->fail_son.push_back(it->second);
		q[++r]=it->second;
	}
	while(r!=h)
	{
		Trie *p=q[++h];
		for(it=p->son.begin();it!=p->son.end();it++)
		{
			Trie *temp=p->fail;
			while(temp!=root&&!temp->son[it->first])
				temp=temp->fail;
			if(temp->son[it->first])
				temp=temp->son[it->first];
			it->second->fail=temp;
			temp->fail_son.push_back(it->second);
			q[++r]=it->second;
		}
	}
}
void DFS(Trie *p)
{
	vector<Trie*>::iterator it;
	set<int>::iterator _it;
	for(it=p->fail_son.begin();it!=p->fail_son.end();it++)
	{
		DFS(*it);
		set<int>&s=p->s;
		set<int>&son_s=(*it)->s;
		if( s.size()<son_s.size() )
			swap(s,son_s);
		for(_it=son_s.begin();_it!=son_s.end();_it++)
			s.insert(*_it);
		son_s.clear();
	}
	if(p->ed)
	{
		ans1[p->ed]=p->s.size();
		for(_it=p->s.begin();_it!=p->s.end();_it++)
			ans2[*_it]+=p->cnt;
	}
}
int main()
{
	int i,x;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&x);
		Insert(root,x,i,0);
		scanf("%d",&x);
		Insert(root,x,i,0);
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d",&x);
		Insert(root,x,i,1);
	}
	Build_Tree();
	DFS(root);
	for(i=m;i;i--)
		if(substitute[i])
			ans1[i]=ans1[substitute[i]];
	for(i=1;i<=m;i++)
		printf("%d\n",ans1[i]);
	for(i=1;i<=n;i++)
		printf("%d%c",ans2[i],i==n?'\n':' ');
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值