【[BJOI2017]魔法咒语】
矩阵乘法+\(AC\)自动机
是道很不错的题了
首先是前六十分,就是一个\(AC\)自动机上的套路\(dp\),设\(dp[i][j]\)表示匹配出的长度为\(i\)在自动机上位置为\(j\)的方案数,转移的话就枚举下一个单词选择哪个放到自动机上一波匹配就好了
后面\(40\)分强行变成了另外一道题,\(L\)变成了\(1e8\),一看就是矩乘的复杂度了
但是单词的长度都非常小,于是转移\(dp[i][j]\)的时候只需要从\(dp[i-1][]\)和\(dp[i-2][]\)里转移,发现这非常像斐波那契的转移,于是提前在\(ac\)机上的每个位置都处理一下对应的转移之后矩乘就好了
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define re register
#define LL long long
#define maxn 205
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const LL mod=1e9+7;
char S[maxn];
int fail[maxn],flag[maxn],son[maxn][26];
char T[55][maxn],len[maxn];
int n,m,L,cnt;
inline void ins()
{
scanf("%s",S+1);
int len=strlen(S+1),now=0;
for(re int i=1;i<=len;i++)
{if(!son[now][S[i]-'a']) son[now][S[i]-'a']=++cnt;now=son[now][S[i]-'a'];}
flag[now]=1;
}
inline void Build()
{
std::queue<int> q;
for(re int i=0;i<26;i++) if(son[0][i]) q.push(son[0][i]);
while(!q.empty())
{
int k=q.front();q.pop();
flag[k]|=flag[fail[k]];
for(re int i=0;i<26;i++)
if(son[k][i]) fail[son[k][i]]=son[fail[k]][i],q.push(son[k][i]);
else son[k][i]=son[fail[k]][i];
}
}
namespace solve1
{
int dp[maxn][maxn];
inline int query(int x,int y)
{
int now=x;
for(re int i=1;i<=len[y];i++)
{
if(flag[now]) return -1;
now=son[now][T[y][i]-'a'];
}
if(flag[now]) return -1;
return now;
}
inline void work()
{
dp[0][0]=1;
for(re int i=0;i<L;i++)
for(re int j=0;j<=cnt;j++)
for(re int k=1;k<=n;k++)
{
if(i+len[k]>L) continue;
if(!dp[i][j]) continue;
int v=query(j,k);
if(v==-1) continue;
dp[i+len[k]][v]=(dp[i+len[k]][v]+dp[i][j])%mod;
}
int ans=0;
for(re int i=0;i<=cnt;i++) ans=(ans+dp[L][i])%mod;
printf("%d\n",ans);
}
}
namespace solve2
{
LL ans[maxn][maxn],a[maxn][maxn];
int M;
inline void did_a()
{
LL mid[maxn][maxn];
for(re int i=0;i<=M;i++)
for(re int j=0;j<=M;j++) mid[i][j]=a[i][j],a[i][j]=0;
for(re int k=0;k<=M;k++)
for(re int i=0;i<=M;i++)
for(re int j=0;j<=M;j++)
{a[i][j]+=((mid[i][k]*mid[k][j])%mod);if(a[i][j]>mod) a[i][j]%=mod;}
}
inline void did_ans()
{
LL mid[maxn][maxn];
for(re int i=0;i<=M;i++)
for(re int j=0;j<=M;j++) mid[i][j]=ans[i][j],ans[i][j]=0;
for(re int k=0;k<=M;k++)
for(re int i=0;i<=M;i++)
for(re int j=0;j<=M;j++)
{ans[i][j]+=((a[i][k]*mid[k][j])%mod);if(ans[i][j]>mod) ans[i][j]%=mod;}
}
inline void quick(int b){while(b) {if(b&1) did_ans();b>>=1;did_a();}}
inline void work()
{
M=cnt+cnt+1;
for(re int i=0;i<=cnt;i++)
{
if(flag[i]) continue;
for(re int j=1;j<=n;j++)
if(len[j]==1)
{
int v=son[i][T[j][1]-'a'];
if(!flag[v]) a[v+cnt+1][i+cnt+1]++;
}
else if(len[j]==2)
{
int v=son[i][T[j][1]-'a'];
int vv=son[v][T[j][2]-'a'];
if(flag[v]||flag[vv]) continue;
a[vv+cnt+1][i]++;
}
}
for(re int j=cnt+1;j<=M;j++) a[j-cnt-1][j]++;
for(re int i=0;i<=M;i++) ans[i][i]=1;
quick(L);
LL Ans=0;
for(re int i=cnt+1;i<=M;i++) Ans=(ans[i][cnt+1]+Ans)%mod;
printf("%lld\n",Ans);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&L);
for(re int i=1;i<=n;i++) scanf("%s",T[i]+1),len[i]=strlen(T[i]+1);
for(re int i=1;i<=m;i++) ins();
Build();
if(L<=100) solve1::work();
else solve2::work();
return 0;
}
【[BJOI2017]魔法咒语】的更多相关文章
- bzoj4861 / P3715 [BJOI2017]魔法咒语
P3715 [BJOI2017]魔法咒语 AC自动机+dp+矩阵乘法 常规思路是按基本串建立AC自动机 然鹅这题是按禁忌串建立AC自动机 对后缀是禁忌的点以及它的失配点做上标记$(a[i].ed)$, ...
- [BJOI2017]魔法咒语 --- AC自动机 + 矩阵优化
bzoj 4860 LOJ2180 洛谷P3175 [BJOI2017]魔法咒语 题目描述: Chandra 是一个魔法天才. 从一岁时接受火之教会洗礼之后,Chandra 就显示出对火元素无 ...
- Luogu-3250 [BJOI2017]魔法咒语(AC自动机,矩阵快速幂)
Luogu-3250 [BJOI2017]魔法咒语(AC自动机,矩阵快速幂) 题目链接 题解: 多串匹配问题,很容易想到是AC自动机 先构建忌讳词语的AC自动机,构建时顺便记录一下这个点以及它的所有后 ...
- P3715 [BJOI2017]魔法咒语
P3715 [BJOI2017]魔法咒语 用基本词汇组成\(L\)长度的单词,其中不能包含禁忌词汇 用禁忌词汇建强大的\(tire\)图 解决: 分类讨论,\(L<=100\)用普通dp暴力在\ ...
- [BZOJ4861][BJOI2017]魔法咒语(AC自动机+矩阵优化DP)
4861: [Beijing2017]魔法咒语 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 217 Solved: 105[Submit][Sta ...
- [BJOI2017]魔法咒语
Description Chandra 是一个魔法天才. 从一岁时接受火之教会洗礼之后, Chandra 就显示出对火元素无与伦比的亲和力,轻而易举地学会种种晦涩难解的法术.这也多亏 Chandra ...
- 【题解】AC自动机题解合集
最近貌似大家都在搞字符串?很长一段时间都没有写博客了……还是补一补坑吧. 感觉AC自动机真的非常优美了,通过在trie树上建立fail指针可以轻松解决多模匹配的问题.实际上在AC自动机上的匹配可以看做 ...
- AHOI2018训练日程(3.10~4.12)
(总计:共90题) 3.10~3.16:17题 3.17~3.23:6题 3.24~3.30:17题 3.31~4.6:21题 4.7~4.12:29题 ZJOI&&FJOI(6题) ...
- Trie图(AC自动机)总结
AC自动机构建完成后,某个节点沿着Fail链向上能从长到短走到自己的所有后缀.一般的,遍历主串进行匹配,就是在Trie图上定向移动的过程. 构造(一遍 BFS) void build_AC() { ; ...
随机推荐
- SpringMVC中异常捕获
如果SpringMVC的action中发生异常,我们想将其跳转到一个固定的错误页面,可以通过applicationContext.xml中增加如下配置实现: <bean class=" ...
- Linux下的mysql默认大小写敏感
在Linux下: 1.数据库名与表名是严格区分大小写的: 2.表的别名是严格区分大小写的: 3.列名与列的别名在所有的情况下均是忽略大小写的: 4.变量名也是严格区分大小写的: 在Windows下: ...
- Spring学习手札(三)理解IoC 拯救不开心
Inverse of Control,翻译成“控制反转”,是Spring的核心.IoC不是一种技术,而是一种设计思想.就是将原本在程序中手动创建对象的控制权(new Object() ),交由Spri ...
- HwUI,CMS管理系统模板,漂亮,简单,兼容好
HwUI兼容目前所有浏览器,IE6+,Opera,Firefox,Chorme,Safari,由于IE6基本已废弃,所以也没有测试IE6的兼容,但做了部分IE6兼容调整.HwUI操作简单,路由导航不需 ...
- webpack打包遇到过的问题
1.打包后html文件打开是空白页面,报错信息如图所示: 解决办法:这里主要是将assetsPublicPath的路径从'/'改为'./'就好了. ('/'表示根目录:'./'表示当前目录) 2.运行 ...
- html打造动画【系列2】- 可爱的蛙蛙表情
先感受一下全部表情包: 在开始之前先安利一个知识点:Flex弹性布局 我们一般做水平三列布局都是用的float方法,将每一块浮动显示在同一行.这种方法会导致元素没有原来的高度属性,要用清除浮动来解决空 ...
- React-Native开发之原生模块封装(Android)升级版
本文主题:如何实现原生代码的复用,即如何将原生模块封装. (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/52862 ...
- css3画半圆 , 加上一点动画
border-radius制作半圆与制作圆形的方法是一样的,只是元素的宽度与圆角方位要配合一致,不同的宽度和高度比例,以及圆角方位,可以制作上半圆.下半圆.左半圆和右半圆效果.例如: .semicir ...
- Spring Boot—03REST请求
package com.smartmap.sample.ch1.controller.rest; import java.util.List; import org.apache.commons.lo ...
- Flare-On4 解题复现
01 是一个 html 页面, 用开发者工具看看,发现是简单的 js 加密. 猜测加密算法可逆,试着用 PyvragFvqrYbtvafNerRnfl@syner-ba.pbz 作为输入,然后调试 , ...