文本生成器,密码—题解
两道比较相似的\(AC\)自动机上\(dp\),并且我的做法好像跟题解都不一样。
密码
这个\(dp\),还是挺好设计的。
因为\(AC\)自动机上\(dp\),一般肯定有一维是当前在自动机的某个节点上。
然后发现得状压,因为他让包含所有的串,并且\(N\)很小。
那就直接设计出来了,\(f[i][j][k]\)表示长度为\(i\)的串在自动机的\(j\)节点上时状态为\(k\)的方案数。
转移只要想一想就可以,一般\(AC\)自动机上的\(dp\)都是枚举字符转移的。
其中拿\(st\)存每个节点的状态,很经典的在\(build\)自动机的时候继承信息就行了。
最后答案很显然是\(\sum_{i=0}^{tnt}f[L][i][(1<<N)-1]\)
输出方案最后再说
文本生成器
这题思路跟各位大佬的思路很不一样,感觉我的思路更容易想到一些。
本人思路
这个\(dp\),还是挺好设计的。(貌似在哪见过这句话)
因为\(AC\)自动机上\(dp\),一般肯定有一维是当前在自动机的某个节点上。(同上)
还是上一题的思想,上一题我们可以统计所有\((1<<N)-1\)的答案,这一题怎么办呢?
好纸张的问题,这不是裸的吗,直接设计一维代表他当前到没到带有\(end\)的节点。
还是经典的\(AC\)自动机上从\(fail\)继承信息,就可以知道每个节点是不是一个单词的结尾。
然后类比上题设计状态\(f[i][j][k]\)表示长度为\(i\),在自动机的\(j\)节点上,\(k\)表示当前有没有走到过\(end\)节点,\(0\)代表没有,\(1\)代表有。
然后方程非常好设计
最后答案就是\(\sum_{i=0}^{tnt}f[m][i][1]\)
code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
#define mp make_pair
#define R register int
#define int long
#define printf Ruusupuu = printf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int N = 1e2 + 10 ;
const int K = 6e3 + 10 ;
const int C = 27 ;
const int M = 1e4 + 7 ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline void wap( int &a , int &b ){ a = a ^ b , b = a ^ b , a = a ^ b ; }
inline int mins( int a , int b ){ int zt = b - a ; return a + ( zt & ( zt >> 31 ) ) ; }
inline int maxs( int a , int b ){ int zt = b - a ; return b - ( zt & ( zt >> 31 ) ) ; }
inline int abss( int a ){ int zt = a >> 31 ; return ( a + zt ) ^ zt ; }
int n , m , tr [K][C] , fail [K] , cnt , f [N][K][2] , ans ;
char s [N] ; bool end [K] ;
inline void ins( char s [] ){
int len = strlen( s + 1 ) , x = 0 ;
for( R i = 1 ; i <= len ; i ++ ){
int ch = s [i] - 'A' ;
if( !tr [x][ch] ) tr [x][ch] = ++ cnt ;
x = tr [x][ch] ;
} end [x] = 1 ;
}
void build(){
// for( R i = 0 ; i <= cnt ; i ++ )
// for( R j = 0 ; j < 26 ; j ++ )
// printf( "%ld %ld %ld\n" , i , j , tr [i][j] ) ;
queue< int > q ;
for( R i = 0 ; i < 26 ; i ++ ) if( tr [0][i] ) q.push( tr [0][i] ) ;
while( !q.empty() ){
int x = q.front() ; q.pop() ;
for( R i = 0 ; i < 26 ; i ++ ){
if( tr [x][i] ) fail [tr [x][i]] = tr [fail [x]][i] , end [tr [x][i]] |= end [tr [fail [x]][i]] , q.push( tr [x][i] ) ;
else tr [x][i] = tr [fail [x]][i] ;
}
}
// for( R i = 0 ; i <= cnt ; i ++ )
// for( R j = 0 ; j < 26 ; j ++ )
// printf( "%ld %ld %ld\n" , i , j , tr [i][j] ) ;
}
void sc(){
n = read() , m = read() ;
for( R i = 1 ; i <= n ; i ++ )
scanf( "%s" , s + 1 ) , ins( s ) ;
build() ;
}
void work(){
f [0][0][0] = 1 ;
for( R i = 0 ; i < m ; i ++ )
for( R j = 0 ; j <= cnt ; j ++ )
for( R k = 0 ; k < 26 ; k ++ )
for( R l = 0 ; l <= 1 ; l ++ )
f [i + 1][tr [j][k]][l | end [tr [j][k]]] = ( f [i + 1][tr [j][k]][l | end [tr [j][k]]] + f [i][j][l] ) % M ;
for( R i = 0 ; i <= cnt ; i ++ ) ans = ( ans + f [m][i][1] ) % M ;
printf( "%ld\n" , ans ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
题解思路
统计所有不合法的,最后用\(26^m\)减去就可以了。
和上文一样先搞出来\(end\)标记。
然后如果\(tr[j][p]\)有标记,就不往他那转移(这个思路还是很不错的)
比我的做法快一倍,因为少一个\(2\)的常数。
题解\(link\)
密码输出方案
我的做法
我的做法一看就很伪,不过水过去了。
和最短母串一样的思路,就是搜到他输出完为止。
但是有一个问题,\(f[i][j][k]\)代表的状态并不唯一。
所以我们可以让一个点经过多次,实测只要让他经过小于等于\(10\)次就可以\(AC\)
本题因为要输出所有方案,所以\(dfs\),\(bfs\)都可以。
这个思路还是很值得记录一下的,如果一个点对应的状态不止一个但不多,而我们想不到如何设计新的状态来避免他重复的话,可以让一个状态经过一定的次数,至于这个次数,本地造点大数据调一下参数就可以了。
Dfs code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <iostream>
#include <bitset>
#define mp make_pair
#define R register int
#define int long long
#define printf Ruusupuu = printf
#define scanf Ruusupuu = scanf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int N = 1e2 + 10 ;
const int E = 1e1 + 2 ;
const int M = N * ( 1 << E ) ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline void wap( int &a , int &b ){ a = a ^ b , b = a ^ b , a = a ^ b ; }
inline int mins( int a , int b ){ int zt = b - a ; return a + ( zt & ( zt >> 31 ) ) ; }
inline int maxs( int a , int b ){ int zt = b - a ; return b - ( zt & ( zt >> 31 ) ) ; }
inline int abss( int a ){ int zt = a >> 31 ; return ( a + zt ) ^ zt ; }
int n , l , cnt , anss , tr [M][27] , st [M] , fail [M] , ans , f [10 * E][1 << E][N] , top ;
int fg [E][N][1 << E] ; char s [N] ;
struct P{ char s [N] ; int len ; } m [50] ;
inline void ins( char s [] , int ix ){
int len = strlen( s + 1 ) , x = 0 ;
for( R i = 1 ; i <= len ; i ++ ){
int ch = s [i] - 'a' ;
if( !tr [x][ch] ) tr [x][ch] = ++ cnt ;
x = tr [x][ch] ;
} st [x] |= ( 1 << ( ix - 1 ) ) ;
}
void build(){
queue< int > q ;
for( R i = 0 ; i < 26 ; i ++ ) if( tr [0][i] ) q.push( tr [0][i] ) ;
while( !q.empty() ){
int x = q.front() ; q.pop() ;
for( R i = 0 ; i < 26 ; i ++ ){
if( tr [x][i] ) fail [tr [x][i]] = tr [fail [x]][i] , st [tr [x][i]] |= st [tr [fail [x]][i]] , q.push( tr [x][i] ) ;
else tr [x][i] = tr [fail [x]][i] ;
}
}
}
inline void debug( int x ){ cout << bitset<10> (x) << endl ; }
void dfs( int len , int x , int sa ){
if( len > l ) return ;
if( sa == ( 1 << n ) - 1 && len == l ){
for( R i = 1 ; i <= l ; i ++ )
printf( "%c" , s [i] ) ;
puts( "" ) ; return ;
}
for( R i = 0 ; i < 26 ; i ++ ){
if( fg [len + 1][tr [x][i]][sa | st [tr [x][i]]] <= 10 ){
fg [len + 1][tr [x][i]][sa | st [tr [x][i]]] ++ ;
s [len + 1] = i + 'a' ;
dfs( len + 1 , tr [x][i] , sa | st [tr [x][i]] ) ;
}
}
}
void sc(){
l = read() , n = read() ;
for( R i = 1 ; i <= n ; i ++ )
scanf( "%s" , m [i].s + 1 ) , ins( m [i].s , i ) ;
}
void work(){
build() ;
f [0][0][0] = 1 ;
for( R i = 0 ; i < l ; i ++ )
for( R j = 0 ; j < ( 1 << n ) ; j ++ )
for( R k = 0 ; k <= cnt ; k ++ )
for( R p = 0 ; p < 26 ; p ++ )
f [i + 1][j | st [tr [k][p]]][tr [k][p]] += f [i][j][k] ;
for( R i = 0 ; i <= cnt ; i ++ ) anss += f [l][( 1 << n ) - 1][i] ;
printf( "%lld\n" , anss ) ;
if( anss <= 42 ) fg [0][0][0] = 1 , dfs( 0 , 0 , 0 ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
Bfs code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
#define mp make_pair
#define R register int
#define int long long
#define printf Ruusupuu = printf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int N = 1e2 + 10 ;
const int E = 1e1 + 2 ;
const int M = N * E * ( 1 << E ) ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline void wap( int &a , int &b ){ a = a ^ b , b = a ^ b , a = a ^ b ; }
inline int mins( int a , int b ){ int zt = b - a ; return a + ( zt & ( zt >> 31 ) ) ; }
inline int maxs( int a , int b ){ int zt = b - a ; return b - ( zt & ( zt >> 31 ) ) ; }
inline int abss( int a ){ int zt = a >> 31 ; return ( a + zt ) ^ zt ; }
int n , l , cnt , anss , tr [M][27] , st [M] , fail [M] , ans , ciner , chr [M] , fa [M] , alls , f [3 * E][1 << E][N] ;
int fg [N][N][1 << E] ;
struct P{ char s [N] ; int len ; } m [50] ;
inline bool cmp ( P a , P b ){
int alen = min( a.len , b.len ) ;
for( R i = 1 ; i <= alen ; i ++ ){
if( a.s [i] < b.s [i] ) return 1 ;
if( a.s [i] > b.s [i] ) return 0 ;
} return a.len < b.len ;
}
inline void ins( char s [] , int ix ){
int len = strlen( s + 1 ) , x = 0 ;
for( R i = 1 ; i <= len ; i ++ ){
int ch = s [i] - 'a' ;
if( !tr [x][ch] ) tr [x][ch] = ++ cnt ;
x = tr [x][ch] ;
} st [x] |= ( 1 << ( ix - 1 ) ) ;
}
void build(){
queue< int > q ;
for( R i = 0 ; i < 26 ; i ++ ) if( tr [0][i] ) q.push( tr [0][i] ) ;
while( !q.empty() ){
int x = q.front() ; q.pop() ;
for( R i = 0 ; i < 26 ; i ++ ){
if( tr [x][i] ) fail [tr [x][i]] = tr [fail [x]][i] , st [tr [x][i]] |= st [tr [fail [x]][i]] , q.push( tr [x][i] ) ;
else tr [x][i] = tr [fail [x]][i] ;
}
}
}
void bfs(){
queue< int > qaq, qwq ;
qaq.push( 0 ) , qwq.push( 0 ) ;
fg [0][0][0] = 1 ;
while( !qaq.empty() ){
int x = qaq.front() , sa = qwq.front() ;
qaq.pop() , qwq.pop() ;
// printf( "%ld\n" , x ) ;
if( sa == ( ( 1 << n ) - 1 ) ){
ans ++ ; int cine = ciner , top = 0 , stk [N] ;
memset( stk , 0 , sizeof( stk ) ) ;
while( cine ){
stk [++ top] = chr [cine] ;
cine = fa [cine] ;
} if( top == l ) { for( R i = top ; i ; i -- ) putchar( stk [i] + 'a' ) ; puts( "" ) ; }
}
for( R i = 0 ; i < 26 ; i ++ ){
if( fg [x][tr [x][i]][sa | st [tr [x][i]]] <= 10 ){
fg [x][tr [x][i]][sa | st [tr [x][i]]] ++ ;
chr [++ alls] = i ;
fa [alls] = ciner ;
qaq.push( tr [x][i] ) ;
qwq.push( sa | st [tr [x][i]] ) ;
}
}
ciner ++ ;
}
}
void sc(){
l = read() , n = read() ;
for( R i = 1 ; i <= n ; i ++ )
scanf( "%s" , m [i].s + 1 ) , ins( m [i].s , i ) ;
}
void work(){
build() ;
f [0][0][0] = 1 ;
for( R i = 0 ; i < l ; i ++ )
for( R j = 0 ; j < ( 1 << n ) ; j ++ )
for( R k = 0 ; k <= cnt ; k ++ )
for( R p = 0 ; p < 26 ; p ++ )
f [i + 1][j | st [tr [k][p]]][tr [k][p]] += f [i][j][k] ;
for( R i = 0 ; i <= cnt ; i ++ ) anss += f [l][( 1 << n ) - 1][i] ;
printf( "%lld\n" , anss ) ;
if( anss <= 42 ) bfs() ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
题解做法
由于方案数小于等于\(42\),所以必定不存在一个位置,使得这个位置可以填\(26\)个字母,如果有,那么方案数必定大于\(52\)。
最简单证明:
如果只有一个串 ,
abcd?,?可以随便填,那么就有\(26\)种方案,而把它变成?abcd,就又有\(26\)种方案。
如果多个串之间有一个地方可以随便填abcd?efg,那么交换这两个串必定也是合法方案efg?abcd
所以,需要输出方案的字符串,必定是紧密结合的。
所以只需要预处理出来每两个字符串连接需要多少长度,然后直接\(O(n!)\)枚举就行了。
注意一下,需要把拼接之后长度大于\(L\)的去掉。
code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <iostream>
#include <bitset>
#define mp make_pair
#define R register int
#define int long long
#define printf Ruusupuu = printf
#define scanf Ruusupuu = scanf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int N = 1e2 + 10 ;
const int E = 1e1 + 2 ;
const int M = N * ( 1 << E ) ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline void wap( int &a , int &b ){ a = a ^ b , b = a ^ b , a = a ^ b ; }
inline int mins( int a , int b ){ int zt = b - a ; return a + ( zt & ( zt >> 31 ) ) ; }
inline int maxs( int a , int b ){ int zt = b - a ; return b - ( zt & ( zt >> 31 ) ) ; }
inline int abss( int a ){ int zt = a >> 31 ; return ( a + zt ) ^ zt ; }
int n , l , cnt , anss , tr [M][27] , st [M] , fail [M] , ans , f [10 * E][1 << E][N] ;
int link [50][50] , totl , tops ; // link [i][j]代表i的后缀和j的前缀的匹配长度
char answer [50][50] ;
struct P{ char s [N] ; } m [50] ;
inline void ins( char s [] , int ix ){
int len = strlen( s + 1 ) , x = 0 ;
for( R i = 1 ; i <= len ; i ++ ){
int ch = s [i] - 'a' ;
if( !tr [x][ch] ) tr [x][ch] = ++ cnt ;
x = tr [x][ch] ;
} st [x] |= ( 1 << ( ix - 1 ) ) ;
}
void build(){
queue< int > q ;
for( R i = 0 ; i < 26 ; i ++ ) if( tr [0][i] ) q.push( tr [0][i] ) ;
while( !q.empty() ){
int x = q.front() ; q.pop() ;
for( R i = 0 ; i < 26 ; i ++ ){
if( tr [x][i] ) fail [tr [x][i]] = tr [fail [x]][i] , st [tr [x][i]] |= st [tr [fail [x]][i]] , q.push( tr [x][i] ) ;
else tr [x][i] = tr [fail [x]][i] ;
}
}
}
void sc(){
l = read() , n = read() ;
for( R i = 1 ; i <= n ; i ++ )
scanf( "%s" , m [i].s + 1 ) , ins( m [i].s , i ) , totl += strlen( m [i].s + 1 ) ;
}
void pre(){
for( R i = 1 ; i <= n ; i ++ ){
int leni = strlen( m [i].s + 1 ) ;
for( R j = 1 ; j <= n ; j ++ ){
if( i == j ) continue ;
int fl = 0 , lenj = strlen( m [j].s + 1 ) ;
for( R k = 1 ; k <= leni ; k ++ ){
fl = 0 ;
for( R l = lenj - k + 1 , p = 1 ; l <= lenj ; l ++ , p ++ )
if( m [i].s [p] != m [j].s [l] ) { fl = 1 ; break ; }
if( !fl ) link [j][i] = k ;
}
}
}
}
inline void cpys( int p [] ){
int now = 0 ;
for( R i = 1 ; i <= n ; i ++ ){
int lle = strlen( m [p [i]].s + 1 ) ;
for( R j = link [p [i - 1]][p [i]] + 1 ; j <= lle ; j ++ )
answer [tops][++ now] = m [p [i]].s [j] ;
answer [tops][now + 1] = '\0' ;
}
}
void print(){
int P [50] ;
for( R i = 0 ; i <= n ; i ++ ) P [i] = i ;
do{
int ansl = totl ;
for( R i = 1 ; i <= n ; i ++ )
ansl -= link [P [i]][P [i + 1]] ;
if( ansl == l ) tops ++ , cpys( P ) ;
} while( next_permutation( P + 1 , P + 1 + n ) ) ;
for( R i = 1 ; i <= tops ; i ++ ){
for( R j = i + 1 ; j <= tops ; j ++ ){
// printf( "%s %s %d\n" , answer [i] + 1 , answer [j] + 1 , strcmp( answer [i] , answer [j] ) ) ;
if( strcmp( answer [i] + 1 , answer [j] + 1 ) > 0 )
swap( answer [i] , answer [j] ) ;
}
}
for( R i = 1 ; i <= tops ; i ++ ){
// for( R j = 1 ; j <= l + 1 ; j ++ )
// printf( "%c" , answer [i][j] ) ;
printf( "%s" , answer [i] + 1 ) ;
puts( "" ) ;
}
}
void work(){
build() ;
f [0][0][0] = 1 ;
for( R i = 0 ; i < l ; i ++ )
for( R j = 0 ; j < ( 1 << n ) ; j ++ )
for( R k = 0 ; k <= cnt ; k ++ )
for( R p = 0 ; p < 26 ; p ++ )
f [i + 1][j | st [tr [k][p]]][tr [k][p]] += f [i][j][k] ;
for( R i = 0 ; i <= cnt ; i ++ ) anss += f [l][( 1 << n ) - 1][i] ;
printf( "%lld\n" , anss ) ;
if( anss <= 42 ) pre() , print() ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}

浙公网安备 33010602011771号