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

题目链接:https://vjudge.net/problem/HDU-2243

考研路茫茫——单词情结

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


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

题意:

给出m个单词,问长度不超过n且至少含有1个单词(可重叠)的字符串有多少个?

题解:

1.由于求“>=1”,那么可以先求出“<1”,即“=0”的有多少个,然后再用总的减去,得到答案。

2.“=0”,即不含有任何一个单词,详情请看:POJ2278 DNA Sequence 。

3. 由于长度<=n,那么我们要求 A^1 + A^2 + …… + A^n,其中A是初步得到的矩阵,怎么求?UVA11149 Power of Matrix 。

4. 最后用总的(26+26^2+……+26^n)减去不含单词的(A^1 + A^2 + …… + A^n 的初始状态那一行之和),即为答案。

代码如下:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <vector>
  6 #include <cmath>
  7 #include <queue>
  8 #include <stack>
  9 #include <map>
 10 #include <string>
 11 #include <set>
 12 using namespace std;
 13 typedef unsigned long long LL;
 14 const double EPS = 1e-6;
 15 const int INF = 2e9;
 16 const LL LNF = 9e18;
 17 const int MOD = 1e9+7;
 18 const int MAXN = 30+10;
 19 
 20 int Size;
 21 struct MA
 22 {
 23     LL mat[30][30];
 24     void init()
 25     {
 26         for(int i = 0; i<Size; i++)
 27         for(int j = 0; j<Size; j++)
 28             mat[i][j] = (i==j);
 29     }
 30 };
 31 
 32 MA operator+(const MA &x, const MA &y)
 33 {
 34     MA ret;
 35     memset(ret.mat, 0, sizeof(ret.mat));
 36     for(int i = 0; i<Size; i++)
 37     for(int j = 0; j<Size; j++)
 38         ret.mat[i][j] = x.mat[i][j]+y.mat[i][j];
 39     return ret;
 40 }
 41 
 42 MA operator*(const MA &x, const MA &y)
 43 {
 44     MA ret;
 45     memset(ret.mat, 0, sizeof(ret.mat));
 46     for(int i = 0; i<Size; i++)
 47     for(int j = 0; j<Size; j++)
 48     for(int k = 0; k<Size; k++)
 49         ret.mat[i][j] += 1LL*x.mat[i][k]*y.mat[k][j];
 50     return ret;
 51 }
 52 
 53 MA qpow(MA x, int y)
 54 {
 55     MA s;
 56     s.init();
 57     while(y)
 58     {
 59         if(y&1) s = s*x;
 60         x = x*x;
 61         y >>= 1;
 62     }
 63     return s;
 64 }
 65 
 66 MA solve(MA x, int n)
 67 {
 68     if(n==1) return x;
 69     MA s;
 70     s.init();
 71     s = (s+qpow(x,n/2))*solve(x, n/2);
 72     if(n%2) s = s+qpow(x, n);
 73     return s;
 74 }
 75 
 76 struct Trie
 77 {
 78     const static int sz = 26, base = 'a';
 79     int next[MAXN][sz], fail[MAXN], end[MAXN];
 80     int root, L;
 81     int newnode()
 82     {
 83         for(int i = 0; i<sz; i++)
 84             next[L][i] = -1;
 85         end[L++] = 0;
 86         return L-1;
 87     }
 88     void init()
 89     {
 90         L = 0;
 91         root = newnode();
 92     }
 93     void insert(char buf[])
 94     {
 95         int len = strlen(buf);
 96         int now = root;
 97         for(int i = 0; i<len; i++)
 98         {
 99             if(next[now][buf[i]-base] == -1) next[now][buf[i]-base] = newnode();
100             now = next[now][buf[i]-base];
101         }
102         end[now] = 1;
103     }
104     void build()
105     {
106         queue<int>Q;
107         fail[root] = root;
108         for(int i = 0; i<sz; i++)
109         {
110             if(next[root][i] == -1) next[root][i] = root;
111             else fail[next[root][i]] = root, Q.push(next[root][i]);
112         }
113         while(!Q.empty())
114         {
115             int now = Q.front();
116             Q.pop();
117             end[now] |= end[fail[now]];  //当前串的后缀是否也包含单词
118             for(int i = 0; i<sz; i++)
119             {
120                 if(next[now][i] == -1) next[now][i] = next[fail[now]][i];
121                 else fail[next[now][i]] = next[fail[now]][i], Q.push(next[now][i]);
122             }
123         }
124     }
125 
126     LL query(int n)
127     {
128         MA s;
129         memset(s.mat, 0, sizeof(s.mat));
130         for(int i = 0; i<L; i++)
131         {
132             if(end[i]) continue;  //存在单词的状态没有后继
133             for(int j = 0; j<sz; j++)
134                 if(end[next[i][j]]==0)   //存在单词的状态没有前驱
135                     s.mat[i][next[i][j]]++;  // i到next[i][j]的路径数+1。注意,当next[i][j]==root时,路径数很可能大于1。
136         }
137 
138         Size = L;
139         s = solve(s, n);
140         LL ret = 0;
141         for(int i = 0; i<L; i++)  //答案为:初始状态到各个状态(包括初始状态)的路径数之和。
142             ret += s.mat[0][i];
143         Size = 1;
144         memset(s.mat,0,sizeof(s.mat));  //26+26^2……+26^n。
145         s.mat[0][0] = 26;
146         s = solve(s, n);
147         return s.mat[0][0]-ret;
148     }
149 };
150 
151 Trie ac;
152 char buf[20];
153 int main()
154 {
155     int n, L;
156     while(scanf("%d%d", &n,&L)!=EOF)
157     {
158         ac.init();
159         for(int i = 1; i<=n; i++)
160         {
161             scanf("%s", buf);
162             ac.insert(buf);
163         }
164         ac.build();
165         LL ans = ac.query(L);
166         printf("%llu
", ans);
167     }
168     return 0;
169 }
View Code
原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8455496.html