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

于是Lele想,如果背了N个词根,那这些词根到底会不会在单词里出现呢。更确切的描述是:长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义。

比如一共有2个词根 aa 和 ab ,则可能存在104个长度不超过3的单词,分别为

(2个) aa,ab,

(26个)aaa,aab,aac...aaz,

(26个)aba,abb,abc...abz,

(25个)baa,caa,daa...zaa,

(25个)bab,cab,dab...zab。

这个只是很小的情况。而对于其他复杂点的情况,Lele实在是数不出来了,现在就请你帮帮他。

Input本题目包含多组数据,请处理到文件结束。

每组数据占两行。

第一行有两个正整数N和L。(0<N<6,0<L<2^31)

第二行有N个词根,每个词根仅由小写字母组成,长度不超过5。两个词根中间用一个空格分隔开。

Output对于每组数据,请在一行里输出一共可能的单词数目。

由于结果可能非常巨大,你只需要输出单词总数模2^64的值。

Sample Input

2 3
aa ab
1 2
a

Sample Output

104
52
思路:题目要求不超过L的所有符合单词,我们先考虑等于L
对于所有满足的情况 = 所有情况-不满足情况
不满足情况即是不包含词根的单词,对于计算满足长度所有情况,可以联想到矩阵快速幂,这样我们就只需要建一个矩阵图
对于建图,我们知道,所有以词根作为前缀子串的都是不符合条件的。
从根开始记录每种前缀可以添加的字母路径数(字母不是词根的结尾)。
于是我们可以想到AC自动机,对于自动机补全的trie图,每个单词的fail都指向其最长子后缀
也就是从根以fail指针向下建图的话,当遍历到一个词根末尾时,其下面的有词根都时不合法的,这可以用dfs维护。
我们知道当矩阵中存有边权(代表路径数)时(相当于到每个点走一条路径),我们将自身乘自身,得到的二次幂矩阵,每个位置的权值就相当于从i走到j走两条路径的路径数目。(离散数学)
这样推广到这,就相当于算出矩阵的L次幂,其第一行的和就是从0长度到L长度经过各个路径以各种字母结尾的路径和,这是长度等于L的情况。
那么对于长度<=L的所有情况,我们知道 Σ(A1+A2+A3+...+AL),然后其第一行的和即是答案。
一开始写的是递归,对于L=6为偶数,A1+A2+A3+A3(A1+A2+A3)
         对于L=5为奇数,A1+A2+A2(A1+A2)+A5
但也许是姿势不对,一直tle。 然后看了Discus,我们可以添加一维,(假设原本矩阵为L,此L!=题面的L),使得L+1列全是1,这样对于第L个矩阵maps【1】【L+1】就储存Σ(A1+A2+A3+...+A[L-1])
对于取模,因为是对1<<64取模,太大了直接取不了(睿智的尝试),可以直接把相关数组开成unsign long long 可以直接对溢出舍去相当于取模
还有利用等比数列公式时,由于涉及除法,利用欧拉定理求出逆元即可。 附上丑代码
 #include<bits/stdc++.h>
using namespace std; typedef unsigned long long ull;
struct Node
{
int y,next;
} node[];
int cnt,head[],L;
int n;
ull len;
int tohash[],topos;
void add(int x,int y)
{
node[++cnt].y=y;
node[cnt].next=head[x];
head[x]=cnt;
} struct MAP
{
ull m[][];
friend MAP operator*(const MAP a,const MAP b)
{
MAP tmp;
for(int i=; i<=L+; i++)
{
for(int j=; j<=L+; j++)
{
tmp.m[i][j] = ; for(int k=; k<=L+; k++)
{
tmp.m[i][j] = tmp.m[i][j]+ a.m[i][k]*b.m[k][j];
} }
}
return tmp;
}
// friend MAP operator+(const MAP a,const MAP b)
// {
// MAP tmp;
// for(int i=1;i<=L;i++)
// {
// for(int j=1;j<=L+;j++)
// {
// tmp.m[i][j] = a.m[i][j] + b.m[i][j];
// }
// }
// return tmp;
// }
void init()
{
for(int i=; i<=L+; i++)
{
for(int j=; j<=L+; j++)
{
m[i][j] = ;
}
m[i][i] = ;
}
}
} maps;
struct ACTO
{
int trie[][],tot;
int dp[];
int fail[];
int End[];
void init()
{
tot = cnt = L = topos = ;
memset(trie,,sizeof(trie));
memset(maps.m,,sizeof(maps.m));
memset(head,,sizeof(head));
memset(dp,,sizeof(dp));
memset(fail,,sizeof(fail));
memset(End,,sizeof(End));
memset(tohash,,sizeof(tohash));
}
void Insert(char *str,int num)
{
int len = strlen(str),p=;
for(int i=; i<len; i++)
{
int ch = str[i]-'a';
if(!trie[p][ch])trie[p][ch]=++tot;
p = trie[p][ch];
}
End[p]=num;
}
void get_fail()
{
queue<int>que;
while(!que.empty())que.pop();
for(int i=; i<; i++)if(trie[][i])que.push(trie[][i]);
while(!que.empty())
{
int p = que.front();
que.pop();
for(int i=; i<; i++)
{
if(trie[p][i])
{
fail[trie[p][i]] = trie[fail[p]][i];
que.push(trie[p][i]);
}
else trie[p][i] = trie[fail[p]][i];
}
}
}
void make_edge()
{
for(int i=; i<=tot; i++)
{
add(fail[i],i);
}
}
void dfs(int x,int f)
{
if(End[x])f=;
dp[x] = f;
for(int i=head[x]; i; i=node[i].next)
{
dfs(node[i].y,f);
} }
void graph()
{
for(int i=; i<=tot; i++)
{
if(!dp[i])
{
L++;
if(!tohash[i])tohash[i] = ++topos;
for(int j=; j<; j++)
{
if(!dp[trie[i][j]])
{
if(!tohash[trie[i][j]])tohash[trie[i][j]]=++topos;
maps.m[tohash[i]][tohash[trie[i][j]]]++;
}
}
}
}
}
}; MAP qpow(MAP a,ull b)
{
MAP ans;
ans.init();
MAP base = a;
while(b)
{
if(b&)ans = ans*base;
base = base * base;
b >>= ;
}
return ans;
} ACTO AC; //MAP cal(int l,int r)
//{
//
// if(l==r)return maps;
// int mid = r/2;
// if(r & 1)
// {
// return cal(l,mid)+qpow(maps,mid)*cal(l,mid)+qpow(maps,r);
// }
// else
// {
// return cal(l,mid)+cal(l,mid)*qpow(maps,mid);
// }
//} ull qmulti(ull m,ull n)
{
ull ans = ;
while(n)
{
if(n&)ans += m;
m += m;
n >>= ;
}
return ans;
}
ull qpow(ull a,ull b)
{
ull ans = ;
ull base = a;
while(b)
{
if(b&)ans = qmulti(ans,base);
base = qmulti(base,base);
b >>= ;
}
return ans;
} int main()
{
while(~scanf("%d%llu",&n,&len))
{
AC.init();
memset(maps.m,,sizeof(maps));
char W[];
for(int i=; i<=n; i++)
{
scanf(" %s",W);
AC.Insert(W,i);
}
AC.get_fail();
AC.make_edge();
AC.dfs(,);
AC.graph();
for(int i=;i<=;i++)
{
for(int j=;j<=;j++)
{
printf(" %d ",maps.m[i][j]);
}
puts("");
}
for(int i=;i<=L+;i++)maps.m[i][L+]=;
MAP ans = qpow(maps,len);
ull total = (qpow(,len+)-)*qpow(,(1ull<<)-);
for(int i=;i<=L+;i++)total -= ans.m[][i];
printf("%llu\n",total+);
}
}

HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)的更多相关文章

  1. hdu 2243 考研路茫茫——单词情结 ac自动机+矩阵快速幂

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243 题意:给定N(1<= N < 6)个长度不超过5的词根,问长度不超过L(L <23 ...

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

    题目链接 错的上头了... 这题是DNA的加强版,26^1 +26^2... - A^1-A^2... 先去学了矩阵的等比数列求和,学的是第二种方法,扩大矩阵的方法.剩下就是各种模板,各种套. #in ...

  3. hdu 2243 考研路茫茫——单词情结 AC自动机 矩阵幂次求和

    题目链接 题意 给定\(N\)个词根,每个长度不超过\(5\). 问长度不超过\(L(L\lt 2^{31})\),只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个? 思路 状态(AC自动 ...

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

    题意:长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个. 解题关键:利用补集转化的思想,先求一个词根也不包含的单词个数,然后用总的减去即可.长度不超过L需要用矩阵维数增加一倍 ...

  5. hdu 2243 考研路茫茫——单词情结(AC自动+矩阵)

    考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  6. hdu_2243_考研路茫茫——单词情结(AC自动机+矩阵)

    题目链接:hdu_2243_考研路茫茫——单词情结 题意: 让你求包含这些模式串并且长度不小于L的单词种类 题解: 这题是poj2788的升级版,没做过的强烈建议先做那题. 我们用poj2778的方法 ...

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

    考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  8. HDU 2243 考研路茫茫——单词情结

    考研路茫茫——单词情结 Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HDU. Original ID ...

  9. HDU 2243 考研路茫茫——单词情结 求长度小于等于L的通路总数的方法

    http://acm.hdu.edu.cn/showproblem.php?pid=2243 这是一题AC自动机 + 矩阵快速幂的题目, 首先知道总答案应该是26^1 + 26^2 + 26^3 .. ...

  10. HDU2243 考研路茫茫——单词情结 ——AC自动机、矩阵优化

    题目链接:https://vjudge.net/problem/HDU-2243 考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others)    Memor ...

随机推荐

  1. Java基础知识拾遗(四)

    IO SequenceInputStream,允许链接多个InputStream对象.在操作上该类从第一个InputStream对象进行读取,知道读取完全部内容,然后切换到第二个InputStream ...

  2. hadoop记录-Hadoop参数汇总

    Hadoop参数汇总 linux参数 以下参数最好优化一下: 文件描述符ulimit -n 用户最大进程 nproc (hbase需要 hbse book) 关闭swap分区 设置合理的预读取缓冲区 ...

  3. ACM-ICPC 2018 沈阳赛区网络预赛 B Call of Accepted(表达式求值)

    https://nanti.jisuanke.com/t/31443 题意 给出一个表达式,求最小值和最大值. 表达式中的运算符只有'+'.'-'.'*'.'d',xdy 表示一个 y 面的骰子 ro ...

  4. 自搭的一个系统框架,使用Spring boot+Vue+Element

    基于:jdk1.8.spring boot2.1.3.vue-cli3.4.1 特性:    ~ 数据库访问使用spring data jpa+alibaba druid    ~ 前后端数据交互使用 ...

  5. Java线程池源码解析

    线程池 假如没有线程池,当存在较多的并发任务的时候,每执行一次任务,系统就要创建一个线程,任务完成后进行销毁,一旦并发任务过多,频繁的创建和销毁线程将会大大降低系统的效率.线程池能够对线程进行统一的分 ...

  6. mp的猜猜看

    ~~~~|yjb1072452141---dc9339b4c33103abc4919375203e7a24|A1482583628---0142e0b6090b9b2838328445a79cd1b8 ...

  7. ASP.NET Web API 2 OData v4教程

    程序数据库格式标准化的开源数据协议 为了增强各种网页应用程序之间的数据兼容性,微软公司启动了一项旨在推广网页程序数据库格式标准化的开源数据协议(OData)计划,于此同时,他们还发 布了一款适用于OD ...

  8. 动态解析xml,并生成excel,然后发邮件。

    直接贴代码了! DECLARE @CurrentServer NVARCHAR(100)DECLARE @CurrentDatabase NVARCHAR(100)DECLARE @CurrentLo ...

  9. Codeforces Round #551 (Div. 2) D. Serval and Rooted Tree (树形dp)

    题目链接 题意:给你一个有根树,假设有k个叶子节点,你可以给每个叶子节点编个号,要求编号不重复且在1-k以内.然后根据节点的max,minmax,minmax,min信息更新节点的值,要求根节点的值最 ...

  10. 初始化仓库(git init)

    创建新的仓库 首先进入需要初始化的目录,然后输入git init D:\Git\test λ git init Initialized empty Git repository in D:/Git/t ...