[JZOJ4649] 【NOIP2016提高A组模拟7.17】项链
2024-09-06 08:13:43
题目
描述
题目大意
给你一堆小串,每个小串都有一定的分数。
让你构造一个字符串,若子串中出现了之前的小串,就可以得到对应的分数(可以重复)
问最大分数。
思考历程
一看这题就知道是什么字符串方面的算法。
然后就很自然地想到AC自动机,多串匹配嘛!
接下来就想到建字符串的过程中,一个指针在AC自动机上跳来跳去……
先建出AC自动机,然后算出到达每个节点的贡献。
对于每个节点,枚举字母,若没有出边,就想着用failfailfail往上跳,然后将出边连向那个地方。
从此AC自动机变成了一张有向图,每个点都有262626条出边。题目就转成了在这张有向图上,走mmm步所的最大点权和。
感觉转化到这一步之后就想不出什么了,考虑过最长路一类的做法,都是没有成功。
于是我也不追求满分了,直接跑DP水分。
正解
事实上……
题目中有这么一句话:字符串的长度和不超过200200200。
知道这一切之后我疯了。
既然这样,那不就是一个裸的矩阵乘法吗?
时间就是2003lgm200^3\lg m2003lgm,在6000ms中肯定是可以过的。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
int n;
long long m;
int a[210];
char str[210];
struct Node{
int c[26],fail;
int v;
} d[210];
int cnt,root;
inline void insert(char *s,int val){
int t=root;
for (;*s;++s){
if (!d[t].c[*s-'a'])
d[t].c[*s-'a']=++cnt;
t=d[t].c[*s-'a'];
}
d[t].v+=val;
}
inline void build(){//求fail,顺便做了对空儿子的处理。显然这两步是可以放在一起的
static int q[210];
int head=0,tail=1;
d[root].fail=root;
q[1]=root;
do{
int t=q[++head];
for (int i=0;i<26;++i){
if (d[t].c[i])
q[++tail]=d[t].c[i];
if (t==root){
if (d[t].c[i])
d[d[t].c[i]].fail=root;
else
d[t].c[i]=root;
continue;
}
int nxt=d[t].fail;
while (nxt!=root && !d[nxt].c[i])
nxt=d[nxt].fail;
if (d[nxt].c[i]){
if (d[t].c[i]){
d[d[t].c[i]].fail=d[nxt].c[i];
d[d[t].c[i]].v+=d[d[nxt].c[i]].v;
}
else
d[t].c[i]=d[nxt].c[i];
}
else{
if (d[t].c[i])
d[d[t].c[i]].fail=root;
else
d[t].c[i]=root;
}
}
}
while (head!=tail);
}
struct Matrix{
long long mat[210][210];
inline void operator*=(Matrix &b){
static Matrix res;
memset(res.mat,200,sizeof res);
for (int i=1;i<=cnt;++i)
for (int j=1;j<=cnt;++j)
for (int k=1;k<=cnt;++k)
res.mat[i][j]=max(res.mat[i][j],mat[i][k]+b.mat[k][j]);
memcpy(mat,res.mat,sizeof mat);
}
} f;
inline void get_pow(Matrix &x,long long m){
static Matrix res;
memset(res.mat,200,sizeof res);
for (int i=1;i<=cnt;++i)
res.mat[i][i]=0;
for (;m;m>>=1,x*=x)
if (m&1)
res*=x;
memcpy(x.mat,res.mat,sizeof x);
}
int main(){
cnt=root=1;
scanf("%d%lld",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=1;i<=n;++i){
scanf("%s",str);
insert(str,a[i]);
}
build();
memset(f.mat,200,sizeof f);
for (int i=1;i<=cnt;++i)
for (int j=0;j<26;++j)
f.mat[i][d[i].c[j]]=d[d[i].c[j]].v;
get_pow(f,m);
long long ans=0;
for (int i=1;i<=cnt;++i)
ans=max(ans,f.mat[1][i]);
printf("%lld\n",ans);
return 0;
}
总结
不想说什么……
如果不是没有看到,我这题早就AC的……
最新文章
- curl -w,–write-out参数详解
- 虚拟机CentOS-mini安装完成后的网络设置
- java web项目启动时自动加载自定义properties文件
- 《GK101任意波发生器》升级固件发布(版本:1.0.2.build124)
- How to recover after deleting the symbolic link libc.so.6?
- Image控件的简单使用示例1
- 2014年辛星解读Javascript之DOM之冒泡和捕获
- 同花顺核新下单程序的";界面不操作超时时间";的设定
- MySQL 配置文件my.cnf
- MySQL 必知必会学习笔记(常用命令二)
- webstorm11.0下载地址和webstorm11.0破解程序patcher.exe下载使用方法说明 前端IDE工具的利器
- layer.js 注册登录切换的问题
- Java NIO中的通道Channel(一)通道基础
- ra (数论 , 莫比乌斯反演 , 整点统计)
- 34-ssm 最简洁的模板
- Newtonsoft.Json.Linq对象读取DataSet数据
- 【LeetCode】- Length of Last Word(最后一个单词的长度)
- Linux:挂载
- 廖雪峰Java1-2Java程序基础-2变量和数据类型
- js-权威指南学习笔记7
热门文章
- RN书签
- project3_NeedToLoginCalculator(需要进行登陆确认的计算器)
- 简单实用的makefile
- 【洛谷】P1009阶乘之和
- neo4j采坑记
- RDD运行原理
- 服务启动脚本start_boot.sh
- linux命令重定向>;、>;>;、 1>;、 2>;、 1>;>;、 2>;>;、 <;(转)
- Estimation
- eclipse总结source folder和Deployment Assembly部署