题目
思路
考虑一位一位的加入字符,我们只在乎 当前字符串末尾的匹配情况。
既然如此,我们用 A C AC AC 自动机上的一个节点来表示状态就可以了。
现在是一个 d p dp dp 转移。似乎要存储是否曾经出现过词根?
其实不用,只需要用所有情况减去一次都没有出现过的情况即可。
向量中加入一个值,用来求累和——就不需要考虑不转移的情况了。
并且是常系数。所以使用矩阵加速。复杂度 O ( n 6 log L ) \mathcal O(n^6\log L) O(n6logL) 。
代码
重大坑点(我因此 W A WA WA 了很多次):一个节点是否为“没有出现过词根”,要检查其 f a i l fail fail 指针,而不是单纯地考虑当前这个点是否为单词末尾。详见代码 64 64 64 行。
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
inline int readint(){
int a = 0, f = 1; char c = getchar();
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(long long x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar((x%10)^48);
}
# define MB template < typename T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }
const int MaxN = 27;
namespace ACmachine{ // 自动AC机 [\doge]
struct Node{
int son[26], fail, val;
} node[MaxN];
int cntNode;
int newNode(){
int &id = ++ cntNode;
memset(node[id].son,0,26<<2);
node[id].fail = node[id].val = 0;
return id;
}
void clear(){ cntNode = -1; newNode(); }
void insert(char s[],int val){
int o = 0;
for(int i=0; s[i]!='\0'; ++i){
int &d = node[o].son[s[i]-'a'];
if(d == 0) d = newNode(); o = d;
}
node[o].val = val;
}
int q[MaxN];
void build(){
int *head = q, *tail = q;
for(int i=0; i<26; ++i)
if(node[0].son[i] != 0)
*(tail ++) = node[0].son[i];
while(head != tail){
int o = *(head ++);
for(int i=0; i<26; ++i){
int d = node[node[o].fail].son[i];
if(node[o].son[i] != 0){
node[node[o].son[i]].fail = d;
*(tail ++) = node[o].son[i];
} else node[o].son[i] = d;
}
// 重大坑点!这个点即使不是直接的末尾,实际上也匹配完成了!
getMax(node[o].val,node[node[o].fail].val);
// 重大坑点!这个点即使不是直接的末尾,实际上也匹配完成了!
}
}
} using namespace ACmachine;
typedef unsigned long long int_;
struct Matrix{
int_ a[MaxN][MaxN];
void clear(){ memset(a,0,MaxN*MaxN<<3); }
Matrix operator * (const Matrix &b) const {
Matrix c; c.clear();
for(int i=0; i<MaxN; ++i)
for(int k=0; k<MaxN; ++k)
for(int j=0; j<MaxN; ++j)
c.a[i][j] += a[i][k]*b.a[k][j];
return c;
}
Matrix &operator *= (const Matrix &b){
return *this = (*this)*b;
}
};
Matrix qkpow(Matrix base,int_ q){
Matrix ans = base; -- q;
for(; q; q>>=1,base*=base)
if(q&1) ans *= base;
return ans;
}
int_ dengbi(int_ base,int q){
int_ ans = 0, mi = 1;
for(int i=30; ~i; --i){
ans *= mi+1, mi *= mi;
if(q>>i&1)
ans += (mi *= base);
}
return ans;
}
char s[10]; int n, L;
Matrix movement, origin;
int main(){
while(cin >> n >> L){
clear(), movement.clear();
for(int i=1; i<=n; ++i)
cin >> s, insert(s,i);
build(), origin.clear();
for(int i=0; i<=cntNode; ++i){
if(node[i].val > 0) continue;
for(int j=0; j<26; ++j)
movement.a[i][node[i].son[j]] ++;
}
for(int i=0; i<=cntNode; ++i)
if(node[i].val == 0)
movement.a[i][MaxN-1] = -1; // 统计答案
movement.a[MaxN-1][MaxN-1] = 1; // 求累加
origin.a[0][0] = 1; // 从 null 出发
origin.a[0][MaxN-1] = dengbi(26,L)+1;
origin *= qkpow(movement,int_(L)+1ull);
cout << origin.a[0][MaxN-1] << endl;
}
return 0;
}