hdu2243之AC自动机+矩阵乘法
考研路茫茫——单词情结
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2789 Accepted Submission(s): 782
一天,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实在是数不出来了,现在就请你帮帮他。
每组数据占两行。
第一行有两个正整数N和L。(0<N<6,0<L<2^31)
第二行有N个词根,每个词根仅由小写字母组成,长度不超过5。两个词根中间用一个空格分隔开。
由于结果可能非常巨大,你只需要输出单词总数模2^64的值。
aa ab
1 2
a
52
/*
分析:相信做过poj2778的都知道如何求长度为n的模式串不包含病毒串的个数
没做过的建议去做,此题是poj2778的加强版
本题只需要求出长度<=n的所有串-包含病毒串的个数
即26^1+26^2+26^3+...+26^n-(A^1+A^2+A^3+...+A^n);//A是状态矩阵,即在满足条件下到达另一个状态的个数
26^1+...+26^n可以用快速幂求出h或者矩阵快速幂求出,A^1+...+A^n可以用矩阵二分快速幂求出或者构造:
|1 26| |Sn | |Sn+1 |
|0 26|*|26^n|=|26^(n+1)|;//Sn=26^1+26^2+...+26^n |A 1| |Sn| |Sn+1|
|0 1|*| A|=|A |;//Sn=A+A^2+A^3+...+A^n 只要:|A 1|
|0 1|
自乘n次与|S0|相乘即可,则可以用矩阵快速幂求
|A |
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std; const int MAX=30+10;
//unsigned __int64 mod=1ll<<64;
unsigned __int64 array[MAX][MAX],sum[MAX][MAX],temp[MAX][MAX],ans[MAX][MAX];
__int64 l;
int size,n;
char s[10]; struct TrieNode{
bool mark;//标记是否是词根
int id;//记录节点序号
TrieNode *fail,*next[26];
}*root,Node[MAX]; TrieNode *New_TrieNode(){
memset(&Node[size],0,sizeof(TrieNode));
Node[size].id=size;
return &Node[size++];
} void InsertNode(char *a){//插入词根
TrieNode *p=root;
while(*a){
if(!p->next[(*a)-'a'])p->next[(*a)-'a']=New_TrieNode();
p=p->next[(*a)-'a'];
++a;
}
p->mark=true;
} void Build_AC(){//建立AC自动机并构造初始矩阵array
memset(array,0,sizeof array);
TrieNode *p,*next;
queue<TrieNode *>q;
q.push(root);
while(!q.empty()){
p=q.front();
q.pop();
for(int i=0;i<26;++i){
if(p->next[i]){
next=p->fail;
while(next && !next->next[i])next=next->fail;
p->next[i]->fail=next?next->next[i]:root;
if(p->next[i]->fail->mark)p->next[i]->mark=true;//表示这个前缀是词根,acg,ac
q.push(p->next[i]);
}else p->next[i]=(p == root)?root:p->fail->next[i];//从p->id状态可以递推到p->fail->next[i]状态
if(!p->next[i]->mark)++array[p->id][p->next[i]->id];//表示到达的下一个状态非词根,则可以到达
}
}
} void MatrixInit(unsigned __int64 a[MAX][MAX],bool flag){//矩阵初始化
for(int i=0;i<size;++i){
for(int j=0;j<size;++j){
if(flag)a[i][j]=array[i][j];//a=array
else a[i][j]=(i == j);//a=1
}
}
} void MatrixAdd(unsigned __int64 a[MAX][MAX],unsigned __int64 b[MAX][MAX]){//矩阵相加,a=a+b
for(int i=0;i<size;++i){
for(int j=0;j<size;++j)a[i][j]+=b[i][j];
}
} void MatrixMult(unsigned __int64 a[MAX][MAX],unsigned __int64 b[MAX][MAX]){//矩阵相乘,a=a*b
unsigned __int64 c[MAX][MAX]={0};
for(int i=0;i<size;++i){
for(int j=0;j<size;++j){
for(int k=0;k<size;++k){
c[i][j]+=a[i][k]*b[k][j];
}
}
}
for(int i=0;i<size;++i){
for(int j=0;j<size;++j)a[i][j]=c[i][j];
}
} void MatrixPow(__int64 k){
MatrixInit(sum,0);//sum=1
MatrixInit(temp,1);//temp=array
while(k){
if(k&1)MatrixMult(sum,temp);
MatrixMult(temp,temp);
k>>=1;
}
} void MatrixSum(__int64 k){//A^1+A^2+A^3+...+A^n
if(k == 1){MatrixInit(ans,1);return;}
MatrixSum(k/2);
MatrixPow((k+1)/2);//这里用到了k+1,而k+1可能会超int,所以k即l要用64位
if(k&1){//A+(A+A^m)*(A^1+A^2+...);//m=(k+1)/2
MatrixInit(temp,1);//temp=A;
MatrixAdd(sum,temp);//sum=sum+temp=A^m+A
MatrixMult(ans,sum);//ans=ans*sum=(A^1+A^2+...)*(A^m+A)
MatrixAdd(ans,temp);//ans=ans+temp=ans+A=A^1+A^2+...)*(A^m+A)
}else{//(1+A^m)*(A^1+A^2+...);//m=(k+1)/2
MatrixInit(temp,0);//temp=1
MatrixAdd(temp,sum);//temp=temp+sum=1+A^m
MatrixMult(ans,temp);//ans=ans*temp=(A^1+A^2+...)*(1+A^m)
}
} int main(){
while(scanf("%d%I64d",&n,&l)!=EOF){
size=2;
array[0][0]=1,array[0][1]=26;
array[1][0]=0,array[1][1]=26;
MatrixPow(l);//求26^1+26^2+...+26^l
unsigned __int64 all=sum[0][1];
printf("%I64u\n",all);
size=0;
root=New_TrieNode();
for(int i=0;i<n;++i){
scanf("%s",s);
InsertNode(s);
}
Build_AC();
MatrixSum(l);//A^1+A^2+A^3+...+A^n
for(int i=0;i<size;++i)all-=ans[0][i];
printf("%I64u\n",all);
}
return 0;
}
第二种方法:用包含矩阵的矩阵进行快速幂求A^1+A^2+A^3+...+A^l;//第一次这种方式写,不知道是不是我写错了,感觉效率增加不是很多,为什么别人说效率会增加4倍左右呢,有知道的大神请指教
/*
分析:相信做过poj2778的都知道如何求长度为n的模式串不包含病毒串的个数
没做过的建议去做,此题是poj2778的加强版
本题只需要求出长度<=n的所有串-包含病毒串的个数
即26^1+26^2+26^3+...+26^n-(A^1+A^2+A^3+...+A^n);//A是状态矩阵,即在满足条件下到达另一个状态的个数
26^1+...+26^n可以用快速幂求出h或者矩阵快速幂求出,A^1+...+A^n可以用矩阵二分快速幂求出或者构造:
|1 26| |Sn | |Sn+1 |
|0 26|*|26^n|=|26^(n+1)|;//Sn=26^1+26^2+...+26^n |A 1| |Sn| |Sn+1|
|0 1|*| A|=|A |;//Sn=A+A^2+A^3+...+A^n 只要:|A 1|
|0 1|
自乘n次与|S0|相乘即可,则可以用矩阵快速幂求
|A |
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std; const int MAX=30+10;
//unsigned __int64 mod=1ll<<64;
unsigned __int64 array[MAX][MAX],sum[2][2][MAX][MAX],temp[2][2][MAX][MAX];
int size,n;
__int64 l;
char s[10]; struct TrieNode{
bool mark;//标记是否是词根
int id;//记录节点序号
TrieNode *fail,*next[26];
}*root,Node[MAX]; TrieNode *New_TrieNode(){
memset(&Node[size],0,sizeof(TrieNode));
Node[size].id=size;
return &Node[size++];
} void InsertNode(char *a){//插入词根
TrieNode *p=root;
while(*a){
if(!p->next[(*a)-'a'])p->next[(*a)-'a']=New_TrieNode();
p=p->next[(*a)-'a'];
++a;
}
p->mark=true;
} void Build_AC(){//建立AC自动机并构造初始矩阵array
memset(array,0,sizeof array);
TrieNode *p,*next;
queue<TrieNode *>q;
q.push(root);
while(!q.empty()){
p=q.front();
q.pop();
for(int i=0;i<26;++i){
if(p->next[i]){
next=p->fail;
while(next && !next->next[i])next=next->fail;
p->next[i]->fail=next?next->next[i]:root;
if(p->next[i]->fail->mark)p->next[i]->mark=true;//表示这个前缀是词根,acg,ac
q.push(p->next[i]);
}else p->next[i]=(p == root)?root:p->fail->next[i];//从p->id状态可以递推到p->fail->next[i]状态
if(!p->next[i]->mark)++array[p->id][p->next[i]->id];//表示到达的下一个状态非词根,则可以到达
}
}
} void MatrixInit(unsigned __int64 A[2][2][MAX][MAX],bool flag){//矩阵初始化
for(int a=0;a<2;++a){
for(int b=0;b<2;++b){
for(int i=0;i<size;++i){
for(int j=0;j<size;++j){
if(flag){
if(a+b == 0)A[a][b][i][j]=array[i][j];//A[0][0]=array
else if(b == 1)A[a][b][i][j]=(i == j);//A[0][1]=A[1][1]=1
else A[a][b][i][j]=0;//A[1][0]=0
}else{
if(a == b)A[a][b][i][j]=(i == j);//A[0][0]=A[1][1]=1
else A[a][b][i][j]=0;//A[0][1]=A[1][0]=0;
}
}
}
}
}
} void MatrixMult(unsigned __int64 A[2][2][MAX][MAX],unsigned __int64 B[2][2][MAX][MAX]){//矩阵相乘,a=a*b
unsigned __int64 C[2][2][MAX][MAX]={0};
for(int a=0;a<2;++a){
for(int b=0;b<2;++b){
for(int c=0;c<2;++c){
for(int i=0;i<size;++i){
for(int j=0;j<size;++j){
for(int k=0;k<size;++k){
C[a][b][i][j]+=A[a][c][i][k]*B[c][b][k][j];
}
}
}
}
}
}
for(int a=0;a<2;++a){
for(int b=0;b<2;++b){
for(int i=0;i<size;++i){
for(int j=0;j<size;++j)A[a][b][i][j]=C[a][b][i][j];
}
}
}
} void MatrixPow(__int64 k){
MatrixInit(sum,0);//sum=1
MatrixInit(temp,1);//temp=B=|A 1|
while(k){ //|0 1|
if(k&1)MatrixMult(sum,temp);
MatrixMult(temp,temp);
k>>=1;
}
} unsigned __int64 FastPow(unsigned __int64 a,int k){
unsigned __int64 ans=1;
while(k){
if(k&1)ans=ans*a;
a=a*a;
k>>=1;
}
return ans;
} unsigned __int64 FastSum(__int64 k){
if(k == 1)return 26;
unsigned __int64 ans=FastSum(k/2);
unsigned __int64 a=FastPow(26ull,(k+1)/2);//这里用到了k+1,而k+1可能会超int,k要用64位
if(k&1)return 26+(26+a)*ans;//26+(26+26^m)*(26^1+26^2+...),m=(k+1)/2
else return (1+a)*ans;//(1+26^m)*(26^1+26^2+...),m=(k+1)/2
} int main(){
while(scanf("%d%I64d",&n,&l)!=EOF){
size=0;
root=New_TrieNode();
for(int i=0;i<n;++i){
scanf("%s",s);
InsertNode(s);
}
Build_AC();
MatrixPow(l);
unsigned __int64 ans=FastSum(l);
for(int j=0;j<size;++j){//只要求出最终的sum[0][1][0][i]的结果就行
for(int k=0;k<size;++k){
ans-=sum[0][1][0][k]*array[k][j];
}
}
printf("%I64u\n",ans);
}
return 0;
}
hdu2243之AC自动机+矩阵乘法的更多相关文章
- 【bzoj1444】[Jsoi2009]有趣的游戏 AC自动机+矩阵乘法
题目描述 输入 注意 是0<=P 输出 样例输入 样例输出 题解 AC自动机+矩阵乘法 先将所有字符串放到AC自动机中,求出Trie图. 然后构建邻接矩阵:如果x不是某个字符串的末位置,则x连向 ...
- [BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】
题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j ...
- 【POJ2778】AC自动机+矩阵乘法
DNA Sequence Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14758 Accepted: 5716 Descrip ...
- POJ 2778 DNA Sequence (AC自动机,矩阵乘法)
题意:给定n个不能出现的模式串,给定一个长度m,要求长度为m的合法串有多少种. 思路:用AC自动机,利用AC自动机上的节点做矩阵乘法. #include<iostream> #includ ...
- bzoj 2553: [BeiJing2011]禁忌 AC自动机+矩阵乘法
题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=2553 题解: 利用AC自动机的dp求出所有的转移 然后将所有的转移储存到矩阵中,进行矩阵 ...
- BZOJ 1009 GT考试 (AC自动机 + 矩阵乘法加速dp)
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1009 题意: 准考证号为\(n\)位数\(X_1X_2....X_n(0<=X_ ...
- 洛谷 P4569 - [BJWC2011]禁忌(AC 自动机+矩阵乘法)
题面传送门 又好久没做过 AC 自动机的题了,做道练练手罢( 首先考虑对于某个固定的字符串怎样求出它的伤害,我们考虑贪心,每碰到出现一个模式串就将其划分为一段,最终该字符串的代价就是划分的次数.具体来 ...
- 【poj2778-DNA Sequence】AC自动机+矩阵乘法
题意: (只含AGCT)给定m个病毒串,让你构造一个长度为n的字符串(也只含有AGCT),问有多少种方案.n很大:1<=n<=2000000000 题解: 用病毒串建立AC自动机(num个 ...
- DNA Sequence - POJ 2778(AC自动机+矩阵乘法)
题目大意:DNA序列是有 ATGC 组成的,现在知道一些动物的遗传片段有害的,那么如果给出这些有害的片段,能否求出来所有长度为 N 的基因中有多少是不包含这些有害片段的. 分析:也是断断续续做了一 ...
随机推荐
- jvm学习小结
1. JDK.JRE.JVM之间的关系.JDK包含JRE和其它开发工具库如编译器.调试期,jConsele性能检测工具等2. JVM的构成:类装载器子系统.执行引擎.运行时数据区,如下图: 3. JV ...
- BZOJ 1874 取石子游戏 (NIM游戏)
题解:简单的NIM游戏,直接计算SG函数,至于找先手策略则按字典序异或掉,去除石子后再异或判断,若可行则直接输出. #include <cstdio> const int N=1005; ...
- out/target/common/obj/PACKAGING/public_api.txt android.view.KeyEvent.KEYCODE_has changed value from
编译出错: out/target/common/obj/PACKAGING/public_api.txt:22549: error 17: Field android.view.KeyEvent.KE ...
- 正则RegEXp
JavaScript RegExp 对象 RegExp 对象 RegExp 对象表示正则表达式,它是对字符串执行模式匹配的强大工具. 直接量语法 /pattern/attributes 创建 RegE ...
- Sass入门——简介+语法格式及编译调试
本文来自慕课网大漠. Sass简介 Sass和SCSS区别 1.后缀名不同,很好理解 2.Sass以严格的缩进语法规则书写,不带大括号和分号:而SCSS的语法规则和CSS的语法很类似. Sass: $ ...
- ADO.NET(一) 空间 ADO.NET结构 命名空间(车延禄) System.Data—— 所有的一般数据访问类 S(转载)
ADO.NET(一) 空间 ADO.NET结构 命名空间(车延禄)System.Data—— 所有的一般数据访问类System.Data.Common—— 各个数据提供程序共享(或重写)的类Sys ...
- JS中window.document对象
小知识点注:外面双引号,里面的双引号改为单引号: 在div里面行高设置和整个外面高度一样,才能用竖直居中,居中是行居中 文本框取出来 ...
- Tableau Server 8.0 升级到 8.3 过程记录
一.使用账号(管理员权限),安装文件复制到服务器 二.检查维护状态 如果维护状态过期,更新到新版本会变成未授权. 先进Manage Product Keys刷新一下维护日期(其实不刷新也无所谓.到时候 ...
- linux ln 命令(转载)
ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同不的链接,这个命令最常用的参数是-s,具体用法是:ln –s 源文件 目标文件. 当我们需要在不同的目录,用到相同的 ...
- mysql root密码重置
1.修改my.cnf #位置一般是 /etc/my.cnf 2.重启mysql服务 service mysqld restart 3.进入mysql mysql -uroot -p 然后直接回车 4. ...