题目大意
- 给出n个模板,求长度小于等于m的字符串中有多少个包含这n个模板中至少一个,所有字符串均是小写字母组成,答案对2^64取模。
分析
- 对2^64取模,用unsiged __int64 即可实现自动取模
- 转化为 总的数量-不包含模板的数量
建立AC自动机,构造矩阵M,M[i][j]表示节点i走到节点j只走一步的方法数,从而
ans = (26 + 26^2 + ... + 26^m) - (M + M^2 + ... + M^m)
要求M + M^2 + … + M^m,先构造一个新的矩阵A
A = M M 0 E ,其中E为单位矩阵
求A的m次幂,右上角矩阵即是 M + M^2 + … + M^m
- 对于求26 + 26^2 + … + 26^m,同样可以采用矩阵快速幂求解
- 然后把最终得到的两个结果相减即可,不用考虑会出现负数的情况
代码
/* 给出n个模板,
* 求长度小于等于m的字符串中有多少个包含这n个模板中至少一个
* 所有字符串均是小写字母组成
* 转化为 总的数量-不包含模板的数量
* 答案对 2^64 取模, 去unsigned __int64,即自动取模
* 即 (26 + 26^2 + ... + 26^m) - num
* num = A + A^2 + ... + A^m
* 对模板建立AC自动机,然后构造矩阵A,转化为矩阵M
* A A
* 0 1
* 求M的m次幂,右上角的矩阵即是A + A^2 + ... + A^m
* 求26 + 26^2 + ... + 26^m 也可以用矩阵快速幂求
*/
#include <iostream>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
#define LL unsigned __int64
const int maxn = 80;
const int sigma_size = 26;
struct Matrix {
LL mt[maxn][maxn];
Matrix() {memset(mt , 0 , sizeof(mt));}
Matrix(int n) {
memset(mt , 0 , sizeof(mt));
for(int i = 0; i < n; i++) mt[i][i] = 1;
}
};
struct AC {
int ch[maxn][sigma_size];
int val[maxn]; //val[i]为1表示节点i是不可行状态
int sz;
void Init() {
sz = 1; val[0] = 0;
memset(ch[0] , 0 , sizeof(ch[0]));
}
int idx(char c) {return c - 'a';}
void Insert(string const &s)
{
int cur = 0 , len = s.length();
for(int i = 0; i < len; i++) {
int u = idx(s[i]);
if(!ch[cur][u]) {
memset(ch[sz] , 0 , sizeof(ch[sz]));
val[sz] = 0;
ch[cur][u] = sz++;
}
cur = ch[cur][u];
}
val[cur] = 1;
}
int f[maxn];
void getFail()
{
queue<int> Q;
for(int i = 0; i < sigma_size; i++) {
int u = ch[0][i];
if(u) {Q.push(u); f[u] = 0;}
else ch[0][i] = 0;
}
while(!Q.empty()) {
int cur = Q.front(); Q.pop();
for(int i = 0; i < sigma_size; i++) {
int u = ch[cur][i];
if(!u) {ch[cur][i] = ch[f[cur]][i]; continue;}
Q.push(u);
int v = f[cur];
while(v && !ch[v][i]) v = f[v];
f[u] = ch[v][i];
//若节点的失败指针指向的状态为不可行状态,则该节点也是不可行状态
if(val[f[u]]) val[u] = 1;
}
}
}
int getMatrix(Matrix &M)
{
LL pt[maxn][maxn] = {{0} , {0}} , node[maxn*maxn]; int cnt = 0;
for(int i = 0; i < sz; i++) for(int j = 0; j < sigma_size; j++) {
int u = ch[i][j];
if(!val[u] && !val[i]) pt[i][u]++;
}
for(int i = 0; i < sz; i++) if(!val[i]) node[cnt++] = i;
//构造矩阵M
for(int i = 0; i < cnt; i++) for(int j = 0; j < cnt; j++) {
M.mt[i][j] = pt[node[i]][node[j]];
M.mt[i][j+cnt] = M.mt[i][j];
}
for(int i = cnt; i < 2 * cnt; i++) M.mt[i][i] = 1;
return cnt;
}
};
//n*n的矩阵A*B
Matrix Mat_mul(Matrix const &A , Matrix const &B , int n)
{
Matrix rel;
for(int i = 0; i < n; i++) for(int k = 0; k < n; k++) if(A.mt[i][k]) {
for(int j = 0; j < n; j++) rel.mt[i][j] += A.mt[i][k] * B.mt[k][j];
}
return rel;
}
//n*n的矩阵A^mi
Matrix Mat_pow(Matrix A , int n , int mi)
{
if(1 == n) return A;
Matrix rel(n);
while(mi) {
if(mi&1) rel = Mat_mul(rel , A , n);
A = Mat_mul(A , A , n);
mi >>= 1;
}
return rel;
}
//快速幂求26 + 26^2 + ... + 26^mi
LL get_sum(int mi)
{
Matrix A(2);
A.mt[0][0] = A.mt[0][1] = 26;
A = Mat_pow(A , 2 , mi);
return A.mt[0][1];
}
AC ac;
int main()
{
int m , n;
while(cin >> n >> m)
{
ac.Init();
for(int i = 0; i < n; i++) {
string s; cin >> s;
ac.Insert(s);
}
ac.getFail();
Matrix M;
int cnt = ac.getMatrix(M);
LL ans = get_sum(m) , num = 0;
M = Mat_pow(M , 2 * cnt , m);
/*
for(int i = 0; i < 2 * cnt; i++) {
for(int j = 0; j < 2 * cnt; j++) cout << M.mt[i][j] << " ";
cout << endl;
}
*/
for(int i = cnt; i < 2 * cnt; i++) num += M.mt[0][i];
cout << ans - num << endl;
}
return 0;
}