自动机+矩阵快速幂+二分
题目:考研路茫茫——单词情结
题目大意:给出n个串,求至少包含一个串的长度不操过L的串的个数
思路:首先就是思路转化,自动机一般求的是不包含危险串的字串的个数,因此我们就反过来求,先求所有可能的串26+26^2+26^3…+26^L,然后不可能包含的串的个数,即长度为1,2,。。。,L的不包含给定的n个串的个数。然后用全部的减去这个,就是我们要的答案。
我们知道x^y可用快速幂求,那么幂的前n项和呢?
x^1+x^2+x^3+⋯+x^L=(x^1+x^2+⋯+x^(L/2) )*x^((L+1)/2) (当l是偶数时)
奇数时再加个中间项x^((L+1)/2)便可
这样就可以二分求解幂的前n项和了
对于一个图来说,他的邻接矩阵是A,离散数学告诉我们A^1是两点件距离为1的路径条数,A^2是两点间距离为2的路径条数,那么长度不超过L的距离和便是
A^1+A^2+…+A^L 这个也可用上面的二分进行求解
提交情况:TLE数次,原因长度应该是long long类型
体会 :好题
/*
题目 :http://acm.hdu.edu.cn/showproblem.php?pid=2243
考研路茫茫——单词情结
给出n个串, 求至少包含一个串的长度不操过L的串的个数。利用图的n次幂表示两点间路径长度是n的个数。
再利用二分求幂的前n项和
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 7
#define maxnl 6
#define maxstate 30
#define size 26
typedef unsigned long long u64;
struct matrix{
u64 mat[maxstate][maxstate];
int n, m;
matrix(){n = m = maxstate; }
void init(int _n, int _m){ n = _n, m = _m; }
/* 构造零矩阵 */
void setZero(){ memset(mat, 0, sizeof(mat)); }
/* 构造单位矩阵 */
void setOne(){
for(int i = 0; i < n; ++ i){
for(int j = 0; j < m; ++ j){
mat[i][j] = (i == j);
}
}
}
} matx;
/* 矩阵加法 */
matrix operator + (const matrix &A, const matrix &B){
matrix temp;
temp.init(A.n, A.m);
for(int i = 0; i < A.n; ++ i){
for(int j = 0; j < A.m; ++ j){
temp.mat[i][j] = A.mat[i][j] + B.mat[i][j];
}
}
return temp;
}
/* 矩阵乘法 */
matrix operator * (const matrix &A, const matrix &B){
matrix temp;
temp.init(A.n, B.m);
temp.setZero();
for(int k = 0; k < A.m; ++ k){
for(int i = 0; i < A.n; ++ i){
if(A.mat[i][k])
for(int j = 0; j < B.m; ++ j){
temp.mat[i][j] += A.mat[i][k] * B.mat[k][j];
}
}
}
return temp;
}
/* 矩阵幂 */
matrix operator ^ (matrix A, u64 k){
matrix ans;
ans.init(A.n, A.m);
ans.setOne();
while(k){
if(k & 1) ans = ans * A;
A = A * A;
k >>= 1;
}
return ans;
}
struct automatonNode{
bool real;
int id;
automatonNode* next[size],* fail;
void clear(){
memset(next, NULL, sizeof(next));
real = 0;
id = -1;
}
};
struct automaton{
automatonNode* root, * link,** queue;
int head, tail, ad, tot;
automaton(){
link = new automatonNode[maxn * maxnl];
queue = new automatonNode*[maxn * maxnl];
}
~automaton(){
delete[] link;
delete[] queue;
}
/* 初始化根节点 */
void clear(){
ad = 0;
root = &link[ad ++];
root->clear();
root->fail = root;
}
/* 转换字符 */
int cg(char ch){ return ch - 'a'; }
/* 插入串 */
void insert(char *str){
automatonNode* rt = root;
for(int i = 0; str[i] != '\0'; ++ i){
if(rt->next[cg(str[i])] == NULL){
rt->next[cg(str[i])] = &link[ad ++];
rt->next[cg(str[i])]->clear();
}
rt = rt->next[cg(str[i])];
}
rt->real = 1;
}
/* 建立自动机 */
void built(){
automatonNode * fa;
head = tail = tot = 0;
queue[tail ++] = root;
while(head != tail){
fa = queue[head ++];
if(fa->real == 0 && fa->id == -1) fa->id = tot ++;
for(int i = 0; i < size; ++ i){
if(fa->next[i] == NULL){
fa->next[i] = (fa == root) ? (root) : (fa->fail->next[i]);
}
else{
fa->next[i]->fail = (fa == root) ? (root) : (fa->fail->next[i]);
fa->next[i]->real |= fa->next[i]->fail->real;
queue[tail ++] = fa->next[i];
if(fa->real) fa->next[i]->real = 1;
}
}
}
}
/* 建立自动机的矩阵 */
void buitMatrix(){
automatonNode *rt;
head = 0;
matx.setZero();
matx.init(tot, tot);
while(head < tail){
rt = queue[head ++];
if(rt->real) continue;
for(int i = 0; i < size; ++ i){
if(rt->next[i]->real == 0)
matx.mat[rt->id][rt->next[i]->id] ++;
}
}
}
};
u64 numPowerMod(u64 x, u64 k){
u64 ans = 1;
while(k){
if(k & 1) ans *= x;
x *= x;
k >>= 1;
}
return ans;
}
u64 numSumPowerMod(u64 x, u64 k){
if(k == 1) return x;
u64 sumTemp = numSumPowerMod(x, k >> 1), numTemp = numPowerMod(x, (k + 1) >> 1);
if(k & 1) return sumTemp + sumTemp * numTemp + numTemp;
else return sumTemp + sumTemp * numTemp;
}
matrix sumTemp, numTemp;
matrix matrixSumPowerMod(u64 k){
if(k == 1) return matx;
sumTemp = matrixSumPowerMod(k >> 1);
numTemp = matx ^ ((k + 1) >> 1);
if(k & 1) return sumTemp + sumTemp * numTemp + numTemp;
else return sumTemp + sumTemp * numTemp;
}
void slove(u64 len){
u64 ans = numSumPowerMod(26, len);
matx = matrixSumPowerMod(len);
for(int i = 0; i < matx.m; ++ i){
ans -= matx.mat[0][i];
}
printf("%I64u\n", ans);
}
int main(){
u64 ans;
u64 n, l;
char str[maxnl];
automaton autom;
while(~scanf("%I64u %I64u", &n, &l)){
autom.clear();
for(int i = 0; i < n; ++ i){
scanf("%s", str);
autom.insert(str);
}
autom.built();
autom.buitMatrix();
slove(l);
}
return 0;
}