// hdu 2243 AC自动机 + 矩阵快速幂
//
// 题目大意:
//
// 给你一些短串,问在长度不超过k的任意串,包含至少一个这些短串的其中
// 一个.问这样的串有多少个.
//
// 解题思路:
//
// 首先, 包含和不包含是一种互斥关系,包含+不包含 = 全集u.全集的答案就是
// 26 ^ 1 + 26 ^ 2 + .... + 26 ^ k.不包含的比较好求.构建一个自动机,得到
// 一个转移矩阵A.表示状态i能到状态j的方法数.而这些状态中都是不包含所给的
// 串的.我们对这个矩阵进行k次幂,得到的矩阵的第一行的和,就是长度为k的不包
// 含串的的总数.我们要求的是A + A ^ 2 + A ^ 3 +.... + A ^ k.之后的部分就是
// 矩阵的再次构造.对于矩阵 | A E |
//
// | 0 E | 对其求n次方,得到
//
// | A^n 1 + A + A^2 +...+A^(n-1) |
// | 0 E |
// 对于这一题,我们增加一维构成全是1.即列全是1,行全是0除了处在最后一列上的除外.
// 对于26是一样的求法即 | 26 1 |
// | 0 1 | 对其求n次方,得到
//
// | 26^n 1 + 26 + 26 ^ 2 + ... + 26^(n-1)|
// | 0 1 |
// 这样最后的答案就是全集 - 不包含.(矩阵写的搓,望各位体谅一二 (^ - ^)
//
// 感悟:
//
// 这道题,重点的思维还是在于补集的思想.将不好求的问题转化为好求的问题.自动机的
// 构建通过poj2778已经经历了一定程度的锤炼,不成问题.矩阵的构造也是问题不大,关键
// 是矩阵的n次方的求和.本来有一道裸地矩阵n次方求和,兴奋的学了学大牛的二分思想,
// 结果爆栈了,加了扩充栈以后,tle了...好吧,乖乖地再次构造一个矩阵,添加一维,然后
// 就过了,瞬间觉得矩阵太美妙了~~~~总的来说,这道题还是不错的,继续加油吧~~~FIGHTING
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef unsigned long long ull;
const int MAX_N = 66;
const int SIGMA = 26;
//;const ull MOD = (ull)1 << 64;
struct Matrix {
int r,c;
ull mat[MAX_N][MAX_N];
}res;
struct Aho_Corasick{
int ch[MAX_N][SIGMA];
int f[MAX_N];
int last[MAX_N];;
bool val[MAX_N];
int sz;
void init(){
memset(ch[0],-1,sizeof(ch[0]));
f[0] = 0;
last[0] = 0;
sz = 1;
}
int idx(char c){
return c - 'a';
}
void insert(char *s){
int n = strlen(s);
int u = 0;
for (int i=0;i<n;i++){
int c = idx(s[i]);
if (ch[u][c]==-1){
memset(ch[sz],-1,sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = true;
}
void getfail(){
queue<int> que;
for (int c=0;c<SIGMA;c++){
int u = ch[0][c];
if (u!=-1){
que.push(u);
f[u] = 0;
last[u] = 0;
}
else {
ch[0][c] = 0;
}
}
while(!que.empty()){
int r = que.front();
que.pop();
if (val[f[r]])
val[r] = true;
for (int c = 0;c < SIGMA;c++){
int u = ch[r][c];
if (u==-1){
ch[r][c] = ch[f[r]][c];
continue;
}
que.push(u);
int v = f[r];
while(v && ch[v][c] == -1)
v = f[v];
f[u] = ch[v][c];
last[u] = val[f[u]] ? f[u] : last[f[u]];
}
}
}
void get_Matrix(){
memset(res.mat,0,sizeof(res.mat));
for (int u = 0; u < sz; u ++){
for (int c = 0; c < 26; c++){
if (!val[u] && !val[ch[u][c]])
res.mat[u][ch[u][c]]++;
}
}
for (int i=0;i<sz+1;i++)
res.mat[i][sz]=1;
res.r = sz + 1;
res.c = sz + 1;
//print();
}
}ac;
int n,l;
Matrix Multiply(Matrix a,Matrix b){
Matrix ans;
for (int i=0;i<a.r;i++)
for (int j=0;j<b.c;j++){
ans.mat[i][j] = 0;
for (int k=0;k<a.c;k++)
ans.mat[i][j] += a.mat[i][k] * b.mat[k][j];
}
ans.r = a.r;
ans.c = b.c;
return ans;
}
Matrix Add(Matrix a,Matrix b){
for (int i=0;i<a.r;i++)
for (int j=0;j<a.c;j++)
a.mat[i][j] += b.mat[i][j];
return a;
}
Matrix Power(Matrix a,int b){
Matrix ans;
ans.r = a.r;
ans.c = a.c;
memset(ans.mat,0,sizeof(ans.mat));
for (int i=0;i<a.r;i++)
ans.mat[i][i] = 1;
while(b){
if (b & 1)
ans = Multiply(ans,a);
a = Multiply(a,a);
b>>=1;
}
return ans;
}
//Matrix sigma_Matrix(Matrix res,int k){ // 二分求 A + A^2 + A^3 + ... + A^n
// if (k == 1) return res;
// Matrix B = Power(res,(k+1)/2);
// Matrix C = sigma_Matrix(res,k/2);
// if (k&1){
// return Add(res,Multiply(Add(res,B),C));
// }else {
// return Multiply(Add(Power(res,0),B),C);
// }
//}
ull Power_Int(ull a,int b){
ull res = 1;
while(b){
if (b & 1)
res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
//ull sigma_Int(ull a,int k){ // 二分 求整数 x + x^2 + x^3 + ... + x^n
// if (k==1) return a;
// ull b = Power_Int(a,(k+1)/2);
// ull c = sigma_Int(a,k/2);
//
// //printf("b = %llu c = %llu k = %d\n",b,c,k);
// if (k & 1){
// return a + (a + b) * c;
// }else {
// return (1 + b) * c;
// }
//}
//
//void print(){
// for (int i=0;i<ac.sz;i++){
// for (int j=0;j<ac.sz;j++)
// printf("%llu ",res.mat[i][j]);
// puts("");
// }
//}
void input(){
char s[20];
ac.init();
for (int i=0;i<n;i++){
scanf("%s",s);
ac.insert(s);
}
ac.getfail();
ac.get_Matrix();
// print();
//res = sigma_Matrix(res,l);
res = Power(res,l);
ull ans = 0;
for (int i=0;i<ac.sz+1;i++){
ans += res.mat[0][i];
}
ans--;
res.r = 2;
res.c = 2;
res.mat[0][0] = 26;
res.mat[0][1] = res.mat[1][1] = 1;
res.mat[1][0] = 0;
res = Power(res,l);
ull sum = res.mat[0][1] + res.mat[0][0]-1;
//printf("ans = %llu sum = %llu l = %d\n",ans,sum,l);
printf("%llu\n",sum-ans);
}
//void init(){
// for (int i=0;i<MAX_N;i++)
// x.mat[i][i] = 1;
//}
int main(){
//init();
//freopen("1.txt","r",stdin);
while(scanf("%d%d",&n,&l)!=EOF){
input();
}
}
hdu 2243 AC自动机 + 矩阵快速幂
最新推荐文章于 2020-02-03 16:38:58 发布