【题解】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 ...
随机推荐
- js+canvas五子棋人机大战ai算法
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- @总结 - 7@ 生成树计数 —— matrix - tree 定理(矩阵树定理)与 prüfer 序列
目录 @0 - 参考资料@ @0.5 - 你所需要了解的线性代数知识@ @1 - 矩阵树定理主体@ @证明 part - 1@ @证明 part - 2@ @证明 part - 3@ @证明 part ...
- python selenium 测试 LOG
1.首先在根目录中新建一个Logs文件夹,写入文件 2.在framework文件夹中写入logger.py 3.在testsuits文件夹中写入test_log.py logger.py # _*_ ...
- PHP调用纯真IP数据库返回具体地址
function convertip($ip) { $ip1num = 0; $ip2num = 0; $ipAddr1 =""; $ipAddr2 =""; ...
- H3C 用display interface命令显示接口信息
- 推荐C++程序员阅读《CLR via C#》
这本书的作者Jeffrey Richter也是<Windows核心编程>的作者. <Windows核心编程>更多的是对window系统相关知识的挖掘积累.<CLR via ...
- CodeForces 1204E"Natasha, Sasha and the Prefix Sums"(动态规划 or 组合数学--卡特兰数的应用)
传送门 •参考资料 [1]:CF1204E Natasha, Sasha and the Prefix Sums(动态规划+组合数) •题意 由 n 个 1 和 m 个 -1 组成的 $C_{n+m} ...
- visualStudio 无法登陆
如果遇到 visualStudio 无法登陆,可以看下我的方法,可能有用 尝试关闭代理 打开设置.网络.代理,关了它,试试 如果遇到下面的问题: 我们无法刷新此账户的凭据 No home tenant ...
- 记录vue创建项目过程
已经学过无数次,但是每次都忘记,毕竟脑容量太小了,每次都需要翻看原来项目和视频再次学习,所以以此文字形式记录下来,方便于下次使用观看 1.打开git,找到创建vue的文件夹(已经安装好git的,然后在 ...
- promise 讲解
Promise的出现 解决了 js 回调地狱得问题 回调地狱图 Promise解决回调地狱 是不是美观多了.. 实例化Promise时传入方法里的两个参数 resolve(成功的回调)和reject ...