大家都吼强,可与之共勉 。
题意:
您现在有
n
个单词,您得构造一只长度为
数据范围
n≤60,m≤100
。
题解:补集转化,AC自动机上面DP。
首先我们构造出这
n
个单词的AC自动机,然后考虑到求出能构造出的,我们可以用总方案数减去不能构造出的方案数。
不能构造出的就是在自动机走不到单词的尾节点,于是简单DP求出方案数。
复杂度
补全AC自动机方便死了呢
/**************************************************************
Problem: 1030
User: Lazer2001
Language: C++
Result: Accepted
Time:192 ms
Memory:82372 kb
****************************************************************/
# include <bits/stdc++.h>
# define N 100010
int n, m ;
short dp [6005] [6005] ;
class AhoCrasickAutoMaton {
private :
int ncnt ;
bool ban [N] ;
int go [N] [26], fail [N] ;
public :
inline void insert ( char* s ) {
int cur = 0 ;
for ( char *pt = s ; *pt ; ++ pt ) {
if ( ! go [cur] [*pt - 'A'] ) go [cur] [*pt - 'A'] = ++ ncnt ;
cur = go [cur] [*pt - 'A'] ;
}
ban [cur] = 1 ;
}
void build ( ) {
fail [0] = -1 ;
static std :: queue < int > Q ;
for ( int i = 0 ; i < 26 ; ++ i )
if ( go [0] [i] ) Q.push ( go [0] [i] ) ;
while ( ! Q.empty ( ) ) {
int cur = Q.front ( ) ; Q.pop ( ) ;
for ( int i = 0 ; i < 26 ; ++ i )
if ( go [cur] [i] ) {
Q.push ( go [cur] [i] ) ;
fail [go [cur] [i]] = go [fail [cur]] [i] ;
} else {
go [cur] [i] = go [fail [cur]] [i] ;
}
ban [cur] |= ban [fail [cur]] ;
}
}
int Dp ( ) {
dp [0] [0] = 1 ;
for ( int i = 1 ; i <= m ; ++ i )
for ( int j = 0 ; j <= ncnt ; ++ j ) {
if ( ban [j] ) continue ;
for ( int k = 0 ; k < 26 ; ++ k ) {
dp [i] [go [j] [k]] += dp [i - 1] [j] ;
if ( dp [i] [go [j] [k]] >= 10007 ) dp [i] [go [j] [k]] -= 10007 ;
}
}
int cnt1 = 0, cnt2 = 1 ;
for ( int i = 0 ; i <= ncnt ; ++ i )
if ( ! ban [i] ) {
cnt1 += dp [m] [i] ;
if ( cnt1 >= 10007 ) cnt1 -= 10007 ;
}
for ( int i = 1 ; i <= m ; ++ i ) ( cnt2 *= 26 ) %= 10007 ;
return ( cnt2 - cnt1 + 10007 ) % 10007 ;
}
} T ;
int main ( ) {
scanf ( "%d%d", & n, & m ) ;
while ( n -- ) {
static char s [6005] ;
scanf ( "%s", s ) ;
T.insert ( s ) ;
}
T.build ( ) ;
printf ( "%d\n", T.Dp ( ) ) ;
return 0 ;
}