首先是给了很多个模式串,首选考虑 AC自动机,从而实现对禁止字符串的识别。我们在自动机上走一步,就相当于在字符串末尾追加一个字母。(注:这里在构建 fail 指针时,如果一个节点 u 的 fail 指针指向了非法节点,就意味着当前前缀的某个后缀是非法的,那么当前 u 这一点也必须设置成非法的),如下:
structMatrix { ll m[maxsz][maxsz]; int sz; Matrix( int _sz = 0 ) : sz( _sz ) { memset( m, 0, sizeof m ); } voidinit( ){ for( int i = 1; i <= sz; ++ i ) m[i][i] = 1; } friend Matrix operator *( Matrix a, Matrix b ) { Matrix res( a.sz ); for( int i = 1; i <= a.sz; ++ i ) { for( int j = 1; j <= b.sz; ++ j ) { for( int k = 1; k <= a.sz; ++ k ) { res.m[i][j] = ( res.m[i][j] + a.m[i][k] * b.m[k][j] % mod ) % mod; } } } return res; } };
Matrix mqpow( Matrix a, ll k ){ Matrix res( a.sz ); res.init( ); for( ; k; k >>= 1, a = a * a ) if( k & 1 ) res = res * a; return res; } // 上面是我的矩阵快速幂板子,为什么要贴出来呢?我也不知道
voidsolve( ){ ll n, k; actree.init( ); // 0-index
cin >> n >> k; for( int i = 1; i <= k; ++ i ) { cin >> s[i]; actree.insert( s[i] ); } actree.build( ); int sz = actree.tot + 1;
Matrix A( sz ); // 1-index for( int u = 0; u <= actree.tot; ++ u ) { if( actree.bad[u] ) continue; for( int ch = 0; ch < 26; ++ ch ) { int v = actree.ch[u][ch]; if( !actree.bad[v] ) A.m[v+1][u+1] ++; // 0->1 } }
Matrix res = mqpow( A, n ); ll ans = 0; for( int i = 0; i <= actree.tot; ++ i ) { if( actree.bad[i] ) continue; ans = ( ans + res.m[i+1][1] ) % mod; // 0->1 } cout << ans << '\n'; }