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实在是数不出来了,现在就请你帮帮他。

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 }
View Code

 

以上是关于HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

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

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