Luogu-3250 [BJOI2017]魔法咒语(AC自动机,矩阵快速幂)

题目链接

题解:

多串匹配问题,很容易想到是AC自动机

先构建忌讳词语的AC自动机,构建时顺便记录一下这个点以及它的所有后缀有没有忌讳词语,即对于每个AC自动机上的结点\(x\),\(p[x].p|=p[p[x].f].p\)

然后前半部分分和后半是两道完全不同的题目(滑稽

前60分:

这些部分分的特征是\(L\le 100\)

直接AC自动机上\(dp\)就好了,枚举匹配长度\(i\),当前匹配到的点\(x\),以及后面要匹配的基本词汇\(s[j]\),找到匹配了这个串后到达的点\(y\),如果匹配过程中没有经过忌讳词语结点,就进行转移\(f[i+len[s[j]]][y]+=f[i][x]\)

最后统计下终点在每个点的情况就好了

inline void work(){
memset(f,0,sizeof(f));
f[0][0]=1;
for(int i=0;i<L;i++)
for(int x=0;x<=tot;x++){
for(int j=1;j<=n;j++){
int len=strlen(a[j]+1);
if(i+len>L) continue;
int y=run(x,a[j]);
if(y==-1) continue;
(f[i+len][y]+=f[i][x])%=P;
}
}
int Ans=0;
for(int x=0;x<=tot;x++) (Ans+=f[L][x])%=P;
printf("%d\n",Ans);
}

后40分:

这些测试点的特点就是基本词汇长度小于等于2

\(\sum s[i]\)这么小,\(L\)这么大不禁让人往矩阵快速幂上想

利用矩阵乘法进行转移,如果设被乘的矩阵\(S\)中\(S[0][i]\)代表每个点是否可以在串长为0时匹配(很明显只有\(S[0][0]=1\))。那么如果我们构建乘它的矩阵为\(G\),根据矩阵乘法的运算法则:

\[T[0][j]=\sum_{k=0}^n{S[0][k]*G[k][j]}
\]

可以发现,如果\(G[x][y]\)代表的是走一步从\(x\)转移到\(y\)的方案数,得到的矩阵\(T\)中的元素\(T[0][x]\)就是走一步转移到\(x\)的方案数,也就是相当于模拟走了一步。如果我们走了\(L\)步让\(S*G^L\)就好了,矩阵乘法有结合律,我们就可以先用快速幂算出\(G^L\),最后再乘\(S\)

但是这样做只能处理串长为\(1\)的情况,因为没有保留前一步的方案数,串长为\(2\)的转移是无法处理的。也就是说,设当前是第\(k\)步,转移应该包括两部分:一部分是从\(S_k\)走一步到\(S_{k+1}\),一部分是从\(S_{k-1}\)走两步到\(S_{k+1}\)。

这种需要同时考虑这一步和上一步的矩阵要如何转移呢?

举一个非常简单的例子吧:求斐波那契数列的第\(n\)项。

由于\(Fib[i+1]=Fib[i-1]+Fib[i]\),我们就用了一个大小为\(1*2\)的矩阵\(S\),\(S[0][0]\)记录\(Fib[i-1]\),\(S[0][1]\)记录\(Fib[i]\)。

转移矩阵\(G\)是这样的(矩阵下标从零开始):

\[G=
\left[
\begin{matrix}
0&1\\
1&1
\end{matrix}
\right]
\]

联系\(S[0][0]与S[0][1]\)的含义和矩阵乘法的含义:

\(G[1][0]=1\)代表下一步的\(Fib[i-1]\)就是这一步的\(Fib[i]\)

\(G[1][1]=1\)代表下一步的\(Fib[i]\)能由这一步的\(Fib[i]\)转移

\(G[0][1]=1\)代表下一步的\(Fib[i]\)能由这一步的\(Fib[i-1]\)转移

\(G[0][0]=0\)是因为下一步的\(Fib[i-1]\)已经钦定为这一步的\(Fib[i]\),无需其他转移

总的来说,转移矩阵包括四部分:这一个到下一个(走一步),前一个到下一个(走两步),这一个到“下一个的前一个”(单位矩阵),前一个到下一个的前一个(一般为全\(0\))

了解了矩阵乘法求\(Fib[n]\)的原理之后,这道题就很简单了

构造大小为\(2*n\)的\(S\),\(S[0][0\sim n]\)记前一步的方案数,\(S[0][n+1\sim n+1+n]\)记这一步的方案数

构造大小为\(2n*2n\)的\(G\),右下是长为\(1\)串的转移矩阵,右上是长为\(2\)串的转移矩阵,左下是单位矩阵,左上是全\(0\)矩阵

最后得到的矩阵\(T=S*G^L\)中,\(T[0][n+1\sim n+1+n]\)的和即为答案。

代码:

  • 处理长度分别为\(1,2\)的串的转移
for(int x=0;x<=tot;x++){
for(int i=1;i<=n;i++){
if(strlen(a[i]+1)>1) continue;
int y=run(x,a[i]);
if(y==-1) continue;
g.a[tot+1+x][tot+1+y]++;
}
}
for(int x=0;x<=tot;x++){
for(int i=1;i<=n;i++){
if(strlen(a[i]+1)<2) continue;
int y=run(x,a[i]);
if(y==-1) continue;
g.a[x][tot+1+y]++;
}
}
  • 左下角单位矩阵
	for(int x=0;x<=tot;x++)
g.a[tot+1+x][x]++;
  • 矩阵快速幂
	g=poww(g,L);
int Ans=0;
for(int x=0;x<=tot;x++)
(Ans+=g.a[tot+1][tot+1+x])%=P;
printf("%d\n",(Ans+P)%P);

代码:

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y))
#define qmin(x,y) (x=min(x,y))
#define mp(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int read(){
int ans=0,fh=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ans=ans*10+ch-'0',ch=getchar();
return ans*fh;
}
const int maxn=150,P=1e9+7;
struct node{
int son[26],p,f;
}p[maxn];
struct matrix{
int a[210][210];
}g,base,tmp;
int tot,n,m,L,f[200][maxn];
char a[60][maxn],b[60][maxn];
matrix operator * (matrix x,matrix y){
for(int i=0;i<=tot+1+tot;i++)
for(int j=0;j<=tot+1+tot;j++){
tmp.a[i][j]=0;
for(int k=0;k<=tot+1+tot;k++)
(tmp.a[i][j]+=1ll*x.a[i][k]*y.a[k][j]%P)%=P;
}
return tmp;
}
inline void insert(char *s){
int len=strlen(s+1),x=0;
for(int i=1;i<=len;i++){
int z=s[i]-'a';
if(!p[x].son[z])
p[x].son[z]=++tot;
x=p[x].son[z];
}
p[x].p=1;
}
queue<int>q;
inline void build(){
for(int i=0;i<26;i++)
if(p[0].son[i]) q.push(p[0].son[i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;i++)
if(p[x].son[i]){
p[p[x].son[i]].f=p[p[x].f].son[i];
q.push(p[x].son[i]);
}
else p[x].son[i]=p[p[x].f].son[i];
p[x].p|=p[p[x].f].p;
}
}
inline int run(int x,char *s){
int len=strlen(s+1);
for(int i=1;i<=len;i++){
int z=s[i]-'a';
x=p[x].son[z];
if(p[x].p) return -1;
}
return x;
}
inline void work(){
memset(f,0,sizeof(f));
f[0][0]=1;
for(int i=0;i<L;i++)
for(int x=0;x<=tot;x++){
for(int j=1;j<=n;j++){
int len=strlen(a[j]+1);
if(i+len>L) continue;
int y=run(x,a[j]);
if(y==-1) continue;
(f[i+len][y]+=f[i][x])%=P;
}
}
int Ans=0;
for(int x=0;x<=tot;x++) (Ans+=f[L][x])%=P;
printf("%d\n",Ans);
}
inline matrix poww(matrix x,int y){
for(int i=0;i<=tot+1+tot;i++)
base.a[i][i]=1;
while(y){
if(y&1) base=base*x;
x=x*x,y>>=1;
}
return base;
}
inline void work2(){
for(int x=0;x<=tot;x++){
for(int i=1;i<=n;i++){
if(strlen(a[i]+1)>1) continue;
int y=run(x,a[i]);
if(y==-1) continue;
g.a[tot+1+x][tot+1+y]++;
}
}
for(int x=0;x<=tot;x++){
for(int i=1;i<=n;i++){
if(strlen(a[i]+1)<2) continue;
int y=run(x,a[i]);
if(y==-1) continue;
g.a[x][tot+1+y]++;
}
}
for(int x=0;x<=tot;x++)
g.a[tot+1+x][x]++;
g=poww(g,L);
int Ans=0;
for(int x=0;x<=tot;x++)
(Ans+=g.a[tot+1][tot+1+x])%=P;
printf("%d\n",(Ans+P)%P);
}
int main(){
// freopen("nh.in","r",stdin);
// freopen("zhy.out","w",stdout);
n=read(),m=read(),L=read();
for(int i=1;i<=n;i++) scanf("%s",a[i]+1);
for(int i=1;i<=m;i++) scanf("%s",b[i]+1);
for(int i=1;i<=m;i++) insert(b[i]);
build();
if(L<=100) work();
else work2();
return 0;
}

Luogu-3250 [BJOI2017]魔法咒语(AC自动机,矩阵快速幂)的更多相关文章

  1. [BZOJ4861][BJOI2017]魔法咒语(AC自动机+矩阵优化DP)

    4861: [Beijing2017]魔法咒语 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 217  Solved: 105[Submit][Sta ...

  2. POJ2778 DNA Sequence(AC自动机+矩阵快速幂)

    题目给m个病毒串,问不包含病毒串的长度n的DNA片段有几个. 感觉这题好神,看了好久的题解. 所有病毒串构造一个AC自动机,这个AC自动机可以看作一张有向图,图上的每个顶点就是Trie树上的结点,每个 ...

  3. poj2778DNA Sequence (AC自动机+矩阵快速幂)

    转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud DNA Sequence Time Limit: 1000MS   Memory ...

  4. HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)

    背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab",放在单词前一般 ...

  5. POJ2778(SummerTrainingDay10-B AC自动机+矩阵快速幂)

    DNA Sequence Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 17160   Accepted: 6616 Des ...

  6. poj2778 ac自动机+矩阵快速幂

    给m个子串,求长度为n的不包含子串的母串数,最直接的应该是暴搜,肯定tle,考虑用ac自动机 将子串建成字典树,通过next表来构造矩阵,然后用矩阵快速幂求长度为n的数量 邻接矩阵https://we ...

  7. HDU 2243 考研路茫茫――单词情结 ——(AC自动机+矩阵快速幂)

    和前几天做的AC自动机类似. 思路简单但是代码200余行.. 假设solve_sub(i)表示长度为i的不含危险单词的总数. 最终答案为用总数(26^1+26^2+...+26^n)减去(solve_ ...

  8. POJ - 2778 ~ HDU - 2243 AC自动机+矩阵快速幂

    这两题属于AC自动机的第二种套路通过矩阵快速幂求方案数. 题意:给m个病毒字符串,问长度为n的DNA片段有多少种没有包含病毒串的. 根据AC自动机的tire图,我们可以获得一个可达矩阵. 关于这题的t ...

  9. 考研路茫茫——单词情结 HDU - 2243 AC自动机 && 矩阵快速幂

    背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab",放在单词前一般 ...

  10. POJ 2778 DNA Sequence(AC自动机 + 矩阵快速幂)题解

    题意:给出m个模式串,要求你构造长度为n(n <= 2000000000)的主串,主串不包含模式串,问这样的主串有几个 思路:因为要不包含模式串,显然又是ac自动机.因为n很大,所以用dp不太好 ...

随机推荐

  1. CNBlog客户端--项目介绍以及技术选型

    项目背景 由于现在开始在博客园写博客,再加上我是android程序员!所以呢,就自然而然的想到自己开发一个自己认为"美"的客户端!!其实还有个原因就是最近我比较闲!!纯属自己给自己 ...

  2. 【原】storm源码之mac os x编译twitter storm源码

    twitter storm是由backtype公司创始人nathanmarz一手研发和开源的流计算(实时计算)框架,堪称实时计算领域的hadoop.nathanmarz也是在mac os x环境下开发 ...

  3. 编写高质量代码–改善python程序的建议(二)

    原文发表在我的博客主页,转载请注明出处! 建议七:利用assert语句来发现问题断言(assert)在很多语言中都存在,它主要为调试程序服务,能够快速方便地检查程序的异常或者发现不恰当的输入等,可防止 ...

  4. HTTP错误 401.3

    这是在搭建ASP.NET运行环境时出现的错误,在开启了系统的IIS Web服务器,并安装完.NET FrameWork后, 输入网址:http://127.0.0.1  ,有时会出现401.3的错误, ...

  5. slenium截屏

    创建全屏截屏: public static byte[] takeScreenshot(WebDriver driver) throws IOException { WebDriver augment ...

  6. python迭代器、生成器、yield和xrange

    https://blog.csdn.net/u010138758/article/details/56291013

  7. 安装Centos 7操作系统

    一.安装前准备 VMware workstation.CentOS-7-x86_64 系统镜像. 二.开始新建虚拟机 选择典型-下一步  选择稍后安装操作系统-下一步  选择LINUX-CentOS ...

  8. mysql 客户端命令行下 直接查询并导出数据

    mysql原来还能这么导出数据,涨知识了. 方式1: select ....(sql语句) INTO OUTFILE   '/var/lib/mysql/msg_data.csv ' (导出的文件位置 ...

  9. JsonResponse对象浅析

    JsonResponse   JsonResponse 对象: class JsonResponse(data, encoder=DjangoJSONEncoder, safe=True, json_ ...

  10. PHP引用符&的用法详细解析

    本文转自:http://blog.csdn.net/vip_linux/article/details/10206091PHP中引用符&的用法.关于php的引用(就是在变量或者函数.对象等前面 ...