考研路茫茫——单词情结

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2789    Accepted Submission(s): 782

Problem Description
背单词,始终是复习英语的重要环节。在荒废了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
Author
linle
Recommend
lcy
这道题从上午搞到现在终于是用两种方法搞完了
在这里想说一句,输入的n,l其中l要用到64位,因为后面算26^1+26^2+...+26^l或者A^1+A^2+A^3+...+A^l时要用到(l+1)/2进行二分快速幂,而l+1可能会超int,网上很多都没说清楚
第二个就是求26^1+26^2+...+26^l或者A^1+A^2+A^3+...+A^l都可以用二分进行快速幂或直接进行矩阵快速幂,在这里我两种方法都写了
第三个就是计算26^1+26^2+...+26^l不要用等比公式变成(26^(l+1)-26)/25去进行快速幂计算,这样会出错,至于为什么出错自己调试调试就知道了
第四个就是题目中说结果可能很大需要去mod 2^64,在这里直接定义变量unsigned __int64,这样超出的就自动截断了,相当于mod
分析+题解请看代码
第一种方法:用二分矩阵快速幂求A^1+A^2+...+A^l
/*
分析:相信做过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自动机并构造初始矩阵arraymemset(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,acq.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=arrayelse 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=1MatrixInit(temp,1);//temp=arraywhile(k){if(k&1)MatrixMult(sum,temp);MatrixMult(temp,temp);k>>=1;}
}void MatrixSum(__int64 k){//A^1+A^2+A^3+...+A^nif(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)/2MatrixInit(temp,1);//temp=A;MatrixAdd(sum,temp);//sum=sum+temp=A^m+AMatrixMult(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)/2MatrixInit(temp,0);//temp=1MatrixAdd(temp,sum);//temp=temp+sum=1+A^mMatrixMult(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^lunsigned __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^nfor(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自动机并构造初始矩阵arraymemset(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,acq.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]=arrayelse if(b == 1)A[a][b][i][j]=(i == j);//A[0][1]=A[1][1]=1else 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]=1else 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=1MatrixInit(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)/2else 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自动机+矩阵乘法相关推荐

  1. 【bzoj1444】[Jsoi2009]有趣的游戏 AC自动机+矩阵乘法

    题目描述 输入 注意 是0<=P 输出 样例输入 样例输出 题解 AC自动机+矩阵乘法 先将所有字符串放到AC自动机中,求出Trie图. 然后构建邻接矩阵:如果x不是某个字符串的末位置,则x连向 ...

  2. BZOJ 1009 GT考试 (AC自动机 + 矩阵乘法加速dp)

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1009 题意: 准考证号为\(n\)位数\(X_1X_2....X_n(0<=X_ ...

  3. [BZOJ1444]有趣的游戏(AC自动机+矩阵乘法)

    n个等长字符串,机器会随机输出一个字符串(每个字母出现的概率为p[i]),问每个字符串第一个出现的概率是多少. 显然建出AC自动机,套路地f[i][j]表示i时刻位于节点j的概率. 构建转移矩阵,当i ...

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

    题目链接:点击查看 题目大意:给出 n 个词根,现在要求出长度不大于 len 的单词中,有多少单词包含至少一个词根 题目分析:如果我们反过来想,也就是求出来总的单词数,然后减去不包含词根的单词数,剩下 ...

  5. hdu 3962(AC自动机+矩阵优化dp)

    转载标记处:http://blog.csdn.net/woshi250hua/article/details/7599472 题目大意:给定m个DNA病毒序列,求碱基构成的长度为n且含有两个以上DNA ...

  6. L. Poor God Water(ACM-ICPC 2018 焦作赛区网络预赛,ac自动机+矩阵快速幂 或 BM线性递推)

    描述 God Water likes to eat meat, fish and chocolate very much, but unfortunately, the doctor tells hi ...

  7. POJ - 2778 DNA Sequence(AC自动机+矩阵快速幂)

    题目链接:点击查看 题目大意:给出 n 个长度不大于 10 的字符串表示病毒串,再给出一个长度 len ,问长度为 len 的字符串中,有多少个字符串不含有病毒串作为子串 题目分析:因为病毒串的长度和 ...

  8. POJ 2778 DNA Sequence —— (AC自动机+矩阵快速幂)

    距离上次做AC自动机有很久了=.=,以前这题的思路死活看不懂,现在还是觉得很好理解的. 思路参见:http://blog.csdn.net/morgan_xww/article/details/783 ...

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

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

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

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

最新文章

  1. vs code编辑器
  2. c语言编程高价是啥,有哪位高手可以帮我做几道c语言编程,有钱的呀,价格可以商量...
  3. 【控制】《多无人机协同控制技术》周伟老师-第1章-无人机协同控制技术概述
  4. MySQL ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
  5. bzoj1230[Usaco2008 Nov]lites 开关灯*
  6. php 用积分兑换_PHP积分兑换接口demo
  7. 数据结构排序系列详解之七 归并排序
  8. DQL——数据查询语言
  9. SQL语法基础篇 —— 常用的SQL标准
  10. win10 安装pytorch gpu 及 解决报错“OSError: [WinError 126] 找不到指定的模块
  11. vm内核参数之缓存回收drop_caches
  12. 分享33个超棒的海洋地貌风光图片
  13. 移动硬盘制作DOS启动盘的方法
  14. 【 FlutterUnit 食用指南】 开源篇
  15. IP地址划分, 常见的CIDR划分,子网掩码对应表及在线CIDR工具
  16. 使用easypoi导入导出excel,SSM和SpringBoot通用代码
  17. Kubernetes 常用命令及应用实例
  18. erdas空间建模_ERDAS空间建模工具介绍.ppt
  19. DirectX12的初始化
  20. 适合大一大二学生的深度学习实践项目汇总:涵盖图像处理、语音识别、自然语言处理等领域

热门文章

  1. 一篇带你熟悉MySQL
  2. 计算机专业保研面试备考:概率论
  3. matlab仿真超声波测距,超声波测距仪制作-Arduino中文社区 - Powered by Discuz!
  4. 台式计算机的cpu设置,台式电脑如何超频,cpu超频详细设置教程
  5. android自定义吸顶,Android ScrollView+ViewPager 固定顶部控件,自动吸顶效果
  6. iOS:error: unable to read input file
  7. 苹果备份有什么用_数据备份用什么软件好?好用的数据备份软件分享
  8. 立创EDA能代替传统的PCB设计软件么?比如AD、protel等?可以
  9. 【商业模式学习感悟】趣步App——新型商业模式,还是新型传销?
  10. 分类模型confusion matrix混淆矩阵可视化