【题解】GREWords(AC自动机)
【题解】GREWords(AC自动机)
题目大意:
给定一个由字符串构成的序列,不同位置的字符串有自己权值。现在让你选出一个子序列,使得在这个子序列中,前面的串是后面的串的子串。请你求满足条件的子序列的权值的最大值。一个子序列权值是所有元素权值的和。
考虑一个朴素的DP做法,设\(dp(i)\)表示选择了字符串\(i\)的最大权值子序列,转移直接枚举前面的字符串,通过\(kmp\)判断是否为子串,然后转移,复杂度\(O(n^3)\)
考虑优化一下上面的做法,众所周知,ac自动机\(fail\)树上任意节点到根一条链是一段后缀。那么,一个字符串的所有子串就是它在ac自动机上的所有节点的所有到根链。(子串=自己某段前缀的后缀)
要优化上面那个\(dp\),就是要求我们维护一个数据结构,使得可以根据一个字符串快速查询已有的他所有的子串的权值的最大值。考虑这样一种做法,把\(fail\)树单独拿出来,现在假设我有个\(dp\)值更新就只会对他的子树有影响,考虑到子树的一段连续的\(dfs\)序列,所以直接线段树维护一个区间取\(max\)单点求\(max\)即可。
可以直接先把fail建出来再依次更新\(dp\),使得fail树的形态确定。
总复杂度\(O(n\log n)\)
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=3e5+5;
const int inf=1e9+5;
int to[maxn],w[maxn];
char c[maxn];
string sav[maxn];
namespace Graph{}
namespace ACautomaton{}
namespace SegmentTree{int n,que(const int&,const int&,const int&,const int&);}
namespace Graph{
vector<int> e[maxn];
int dfn[maxn],End[maxn],timer;
inline void add(const int&fr,const int&to){
e[fr].push_back(to);
e[to].push_back(fr);
}
void dfs(const int&now,const int&last){
dfn[now]=++timer;
for(auto t:e[now])
if(t!=last)
dfs(t,now);
End[now]=timer;
}
inline void init(){for(int t=0;t<maxn;++t) e[t].clear(); timer=0;}
}
namespace ACautomaton{
using Graph::add;
struct E{
int son[26],fail;
E(){memset(son,0,sizeof son); fail=0;}
inline int& operator [](int x){return son[x];}
inline int& operator [](char x){return son[x-'a'];}
}ac[maxn];
int cnt;
inline void add(const char*data,const int&n,const int&id){
int now=0;
for(int t=1;t<=n;++t){
if(!ac[now][data[t]]) ac[now][data[t]]=++cnt;
now=ac[now][data[t]];
}
to[id]=now;
}
queue<int> q;
inline void gen(){
queue<int>().swap(q);
for(char c='a';c<='z';++c)
if(ac[0][c])
q.push(ac[0][c]),ac[ac[0][c]].fail=0;
while(q.size()){
int now=q.front(); q.pop();
for(char c='a';c<='z';++c){
if(ac[now][c]) ac[ac[now][c]].fail=ac[ac[now].fail][c],q.push(ac[now][c]);
else ac[now][c]=ac[ac[now].fail][c];
}
}
for(int t=1;t<=cnt;++t) add(t,ac[t].fail);
}
inline void init(){memset(ac,0,sizeof ac); memset(to,0,sizeof to); memset(w,0,sizeof w); cnt=0;}
inline int getans(const string&f){
int now=0,ret=0;
for(const auto&t:f)
ret=max(ret,SegmentTree::que(Graph::dfn[now=ac[now][t]],1,SegmentTree::n,1));
return ret;
}
}
namespace SegmentTree{
#define pp(pos) seg[pos].val=max(seg[pos<<1].val,seg[pos<<1|1].val)
#define mid ((l+r)>>1)
#define lef l,mid,pos<<1
#define rgt mid+1,r,pos<<1|1
struct E{
int val,tag;
E(){val=tag=0;}
E(const int&a,const int&b){val=a; tag=b;}
}seg[maxn<<2];
inline void pd(const int&pos){
if(seg[pos].tag==0) return;
seg[pos<<1].val=max(seg[pos<<1].val,seg[pos].tag);
seg[pos<<1|1].val=max(seg[pos<<1|1].val,seg[pos].tag);
seg[pos<<1].tag=max(seg[pos<<1].tag,seg[pos].tag);
seg[pos<<1|1].tag=max(seg[pos<<1|1].tag,seg[pos].tag);
seg[pos].tag=0;
}
void build(const int&l,const int&r,const int&pos){
seg[pos].val=seg[pos].tag=0;
if(l==r) return;
build(lef); build(rgt);
}
void upd(const int&L,const int&R,const int&k,const int&l,const int&r,const int&pos){
if(L>r||R<l) return;
if(L<=l&&r<=R) {seg[pos].val=max(seg[pos].val,k); seg[pos].tag=max(seg[pos].tag,k); return;}
pd(pos);
upd(L,R,k,lef); upd(L,R,k,rgt);
pp(pos);
}
int que(const int&k,const int&l,const int&r,const int&pos){
if(k<l||k>r) return 0;
if(l==r) return seg[pos].val;
pd(pos);
int ret=max(que(k,lef),que(k,rgt));
pp(pos);
return ret;
}
inline void init(const int&a){n=a;build(1,n,1);}
#undef lef
#undef rgt
#undef mid
}
int main(){
int T=qr(),F=0;
while(T--){
ACautomaton::init();
Graph::init();
int n=qr();
for(int t=1;t<=n;++t){
scanf("%s",c+1);
sav[t]=c+1;
ACautomaton::add(c,strlen(c+1),t);
w[t]=qr();
}
ACautomaton::gen();
Graph::dfs(0,0);
SegmentTree::init(Graph::timer);
int ans=0;
for(int t=1;t<=n;++t){
int k=w[t]+ACautomaton::getans(sav[t]);
SegmentTree::upd(Graph::dfn[to[t]],Graph::End[to[t]],k,1,SegmentTree::n,1);
ans=max(k,ans);
}
printf("Case #%d: %d\n",++F,ans);
}
return 0;
}
【题解】GREWords(AC自动机)的更多相关文章
- CF 1400F x-prime Substrings 题解【AC自动机+DP】
CF 1400F.x-prime Substrings 题意: 给定一个由\('1'\)到\('9'\)组成的字符串\(s\)和一个数\(x\),定义一个串为\(x-prime\)串,当且仅当这个串上 ...
- 【题解】AC自动机题解合集
最近貌似大家都在搞字符串?很长一段时间都没有写博客了……还是补一补坑吧. 感觉AC自动机真的非常优美了,通过在trie树上建立fail指针可以轻松解决多模匹配的问题.实际上在AC自动机上的匹配可以看做 ...
- bzoj3940 censoring 题解(AC自动机)
题目描述 Farmer John has purchased a subscription to Good Hooveskeeping magazine for his cows, so they h ...
- 【JSOI2007】文本生成器 题解(AC自动机+动态规划)
题目链接 题目大意:给定$n$个子串,要求构造一个长度为$m$的母串使得至少有一个子串是其子串.问方案数. ------------------------ 我们可以对要求进行转化:求出不合法的方案数 ...
- 【HDU2896】病毒侵袭 AC自动机
[HDU2896]病毒侵袭 Problem Description 当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻....在这样的时刻,人们却异常兴奋--我们能在有生之年看到500年 ...
- 【HDU2222】Keywords Search AC自动机
[HDU2222]Keywords Search Problem Description In the modern time, Search engine came into the life of ...
- HDU2296——Ring(AC自动机+DP)
题意:输入N代表字符串长度,输入M代表喜欢的词语的个数,接下来是M个词语,然后是M个词语每个的价值.求字符串的最大价值.每个单词的价值就是单价*出现次数.单词可以重叠.如果不止一个答案,选择字典序最小 ...
- BZOJ 1212 [HNOI2004]L语言 【AC自动机 + 背包】
题目链接[http://www.lydsy.com/JudgeOnline/problem.php?id=1212] 题意:给你一些单词,然后给出一个没有标点的文本串S,都是小写字符.现在让你求用给出 ...
- bzoj 3172: [Tjoi2013]单词 AC自动机
3172: [Tjoi2013]单词 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...
随机推荐
- D - Denouncing Mafia DFS
这道题其实很简单,求k个到根的链,使得链上的节点的个数尽可能多,如果节点被计算过了,就不能再被计算了,其实我们发现,只要k>=叶子节点,那么肯定是全部,所以我们考虑所有的叶子节点,DFS到根节点 ...
- 几种map的排序规则
前几天在做理事会成员的时候遇到这个问题,需要根据职务的顺序,放入对应的成员,再把成员名字按首字母拼音顺序排序后,返回给前台解析,之前一直用TreeMap来存放这个排过序的List集合,发现就算我最先循 ...
- AFNetworkingErrorDomain 错误
AFNetworking and POST Request I'm getting this response in error.userInfo while making a POST reques ...
- Datamation Index
Datamation Index Understand how to handle big data and improve organizational agility to support ...
- uni-app获取dom元素到顶部的距离以及操作dom元素的一些样式
一. 1.首先有一个元素 <view class="activity" ref="btn"></view> 2.确认指针指向 this. ...
- OpenStack组件系列☞glance简介
Glance项目提供虚拟机镜像的发现,注册,取得服务. Glance提供restful API可以查询虚拟机镜像的metadata,并且可以获得镜像. 通过Glance,虚拟机镜像可以被存储到多种存储 ...
- 一个简单的hexo搭建博客网站的故事
首先安装hexo mkdir hexo #创建一个文件夹 cd hexo #切换到hexo目录下面 npm install -g hexo-cli npm install hexo --save 然后 ...
- Adam那么棒,为什么还对SGD念念不忘 (3)—— 优化算法的选择与使用策略
在前面两篇文章中,我们用一个框架梳理了各大优化算法,并且指出了以Adam为代表的自适应学习率优化算法可能存在的问题.那么,在实践中我们应该如何选择呢? 本文介绍Adam+SGD的组合策略,以及一些比较 ...
- easyui—element-ui框架套用(表格宽度自适应)
外层使用easyui框架中window组件,便于使用最大化功能:内部表格使用element-ui在的el-table,el-table列宽须设置为最小宽度才能在最大化窗口时列表中列宽自适应window ...
- H3C DHCP系统组成