标签:bzoj bzoj2754 ac自动机 fail树 启发式合并
题目大意:给定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; }
BZOJ 2754 SCOI2012 喵星球上的点名 fail树+set启发式合并
标签:bzoj bzoj2754 ac自动机 fail树 启发式合并
原文地址:http://blog.csdn.net/popoqqq/article/details/41814023