HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)
Posted iwannabe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)相关的知识,希望对你有一定的参考价值。
背单词,始终是复习英语的重要环节。在荒废了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实在是数不出来了,现在就请你帮帮他。
一天,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 可以直接对溢出舍去相当于取模
还有利用等比数列公式时,由于涉及除法,利用欧拉定理求出逆元即可。
附上丑代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 typedef unsigned long long ull; 5 struct Node 6 { 7 int y,next; 8 } node[41]; 9 int cnt,head[40],L; 10 int n; 11 ull len; 12 int tohash[40],topos; 13 void add(int x,int y) 14 { 15 node[++cnt].y=y; 16 node[cnt].next=head[x]; 17 head[x]=cnt; 18 } 19 20 struct MAP 21 { 22 ull m[40][40]; 23 friend MAP operator*(const MAP a,const MAP b) 24 { 25 MAP tmp; 26 for(int i=1; i<=L+1; i++) 27 { 28 for(int j=1; j<=L+1; j++) 29 { 30 tmp.m[i][j] = 0; 31 32 for(int k=1; k<=L+1; k++) 33 { 34 tmp.m[i][j] = tmp.m[i][j]+ a.m[i][k]*b.m[k][j]; 35 } 36 37 } 38 } 39 return tmp; 40 } 41 // friend MAP operator+(const MAP a,const MAP b) 42 // { 43 // MAP tmp; 44 // for(int i=1;i<=L;i++) 45 // { 46 // for(int j=1;j<=L+;j++) 47 // { 48 // tmp.m[i][j] = a.m[i][j] + b.m[i][j]; 49 // } 50 // } 51 // return tmp; 52 // } 53 void init() 54 { 55 for(int i=1; i<=L+1; i++) 56 { 57 for(int j=1; j<=L+1; j++) 58 { 59 m[i][j] = 0; 60 } 61 m[i][i] = 1; 62 } 63 } 64 } maps; 65 struct ACTO 66 { 67 int trie[40][26],tot; 68 int dp[40]; 69 int fail[40]; 70 int End[40]; 71 void init() 72 { 73 tot = cnt = L = topos = 0; 74 memset(trie,0,sizeof(trie)); 75 memset(maps.m,0,sizeof(maps.m)); 76 memset(head,0,sizeof(head)); 77 memset(dp,0,sizeof(dp)); 78 memset(fail,0,sizeof(fail)); 79 memset(End,0,sizeof(End)); 80 memset(tohash,0,sizeof(tohash)); 81 } 82 void Insert(char *str,int num) 83 { 84 int len = strlen(str),p=0; 85 for(int i=0; i<len; i++) 86 { 87 int ch = str[i]-‘a‘; 88 if(!trie[p][ch])trie[p][ch]=++tot; 89 p = trie[p][ch]; 90 } 91 End[p]=num; 92 } 93 void get_fail() 94 { 95 queue<int>que; 96 while(!que.empty())que.pop(); 97 for(int i=0; i<26; i++)if(trie[0][i])que.push(trie[0][i]); 98 while(!que.empty()) 99 { 100 int p = que.front(); 101 que.pop(); 102 for(int i=0; i<26; i++) 103 { 104 if(trie[p][i]) 105 { 106 fail[trie[p][i]] = trie[fail[p]][i]; 107 que.push(trie[p][i]); 108 } 109 else trie[p][i] = trie[fail[p]][i]; 110 } 111 } 112 } 113 void make_edge() 114 { 115 for(int i=1; i<=tot; i++) 116 { 117 add(fail[i],i); 118 } 119 } 120 void dfs(int x,int f) 121 { 122 if(End[x])f=1; 123 dp[x] = f; 124 for(int i=head[x]; i; i=node[i].next) 125 { 126 dfs(node[i].y,f); 127 } 128 129 } 130 void graph() 131 { 132 for(int i=0; i<=tot; i++) 133 { 134 if(!dp[i]) 135 { 136 L++; 137 if(!tohash[i])tohash[i] = ++topos; 138 for(int j=0; j<26; j++) 139 { 140 if(!dp[trie[i][j]]) 141 { 142 if(!tohash[trie[i][j]])tohash[trie[i][j]]=++topos; 143 maps.m[tohash[i]][tohash[trie[i][j]]]++; 144 } 145 } 146 } 147 } 148 } 149 }; 150 151 MAP qpow(MAP a,ull b) 152 { 153 MAP ans; 154 ans.init(); 155 MAP base = a; 156 while(b) 157 { 158 if(b&1)ans = ans*base; 159 base = base * base; 160 b >>= 1; 161 } 162 return ans; 163 } 164 165 ACTO AC; 166 167 //MAP cal(int l,int r) 168 //{ 169 // 170 // if(l==r)return maps; 171 // int mid = r/2; 172 // if(r & 1) 173 // { 174 // return cal(l,mid)+qpow(maps,mid)*cal(l,mid)+qpow(maps,r); 175 // } 176 // else 177 // { 178 // return cal(l,mid)+cal(l,mid)*qpow(maps,mid); 179 // } 180 //} 181 182 183 184 ull qmulti(ull m,ull n) 185 { 186 ull ans = 0; 187 while(n) 188 { 189 if(n&1)ans += m; 190 m += m; 191 n >>= 1; 192 } 193 return ans; 194 } 195 ull qpow(ull a,ull b) 196 { 197 ull ans = 1; 198 ull base = a; 199 while(b) 200 { 201 if(b&1)ans = qmulti(ans,base); 202 base = qmulti(base,base); 203 b >>= 1; 204 } 205 return ans; 206 } 207 208 int main() 209 { 210 while(~scanf("%d%llu",&n,&len)) 211 { 212 AC.init(); 213 memset(maps.m,0,sizeof(maps)); 214 char W[10]; 215 for(int i=1; i<=n; i++) 216 { 217 scanf(" %s",W); 218 AC.Insert(W,i); 219 } 220 AC.get_fail(); 221 AC.make_edge(); 222 AC.dfs(0,0); 223 AC.graph(); 224 for(int i=1;i<=2;i++) 225 { 226 for(int j=1;j<=2;j++) 227 { 228 printf(" %d ",maps.m[i][j]); 229 } 230 puts(""); 231 } 232 for(int i=1;i<=L+1;i++)maps.m[i][L+1]=1; 233 MAP ans = qpow(maps,len); 234 ull total = (qpow(26,len+1)-26)*qpow(25,(1ull<<63)-1); 235 for(int i=1;i<=L+1;i++)total -= ans.m[1][i]; 236 printf("%llu\n",total+1); 237 } 238 }
以上是关于HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)的主要内容,如果未能解决你的问题,请参考以下文章
HDU2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)
hdu2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)
hdu2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)
HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)