您的位置:首页 > 其它

[codeforces 86C]补全AC自动机上DP

2017-10-25 19:28 429 查看

说在前面

microsoftEdge上使用markdown会出现各种排版bug,使用体验极差!

题目

codeforces 86C传送门

题目大意:给定一些长度不超过10的字符串,字符串个数不超过10,询问完全用这些字符串构成(可以重叠,拼接)的长度为N的字符串的方案数。

样例:

Input

6 2

CAT

TACT

Output

2





解法

这道题是dp,me现在仍然没想清楚为什么会这样定义dp数组..

首先对于题目给定的串,建立补全AC自动机。

定义dp[i][j][k]表示现在已经选到了第i个字符,在AC自动机上的j号结点,当前已选串的末尾k个还未被包含在某一个给定字符串内。

预处理每个AC自动机节点作为某个给定字符串的末尾字符时,给定字符串最长的长度,记为Tlen。(相当于是一直跳fail,如果遇到一个节点有isend标记,就用那个串长更新当前点的长度)。

转移:

对于dp[i][j][k]

如果下一个节点的Tlen大于k+1(相当于把之前没有包含的包含了),那么转移到dp[i+1][j`][0]

不然转移到dp[i+1][j`][k+1]

自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

const int mmod = 1e9+9 ;
int N , M , len , dp[1001][101][11] , ans ;
char ss[20] ;
struct Node{
int maxLen , id ;
int cc ;
Node *ch[4] , *fail ;
Node(){
maxLen = 0 ;
ch[0] = ch[1] = ch[2] = ch[3] = fail = NULL ;
}
}w[500] , *root , *tw = w ;

void Insert( ){
Node *nd = root ;
for( int i = 0 ; i < len ; i ++ ){
if( !nd->ch[ ss[i] ] ){
nd->ch[ ss[i] ] = ++tw ;
nd->ch[ ss[i] ]->id = tw - w ;
nd->ch[ ss[i] ]->cc = ss[i] ;
}
nd = nd->ch[ ss[i] ] ;
}
nd->maxLen = len ;
}

Node *q[500] ;
int ba , fr , vis[505] ;
void getFail(){
ba = 0 ; fr = 1 ;
root->fail = root ;
q[++ba] = root ; vis[ root->id ] = true ;
while( ba >= fr ){
Node *u = q[fr++] ;
for( int i = 0 ; i < 4 ; i ++ ){
Node *v = u->ch[i] ;
if( !v || v->fail ) continue ;
if( u == root ) v->fail = root ;
else{
Node *p = u->fail ;
while( !p->ch[i] && p != root ) p = p->fail ;
v->fail = ( p->ch[i] ? p->ch[i] : root ) ;
v->maxLen = max( v->maxLen , v->fail->maxLen ) ;
}
for( int j = 0 ; j < 4 ; j ++ )
if( !v->ch[j] ) v->ch[j] = v->fail->ch[j] ;
q[++ba] = v ;
}
}
}

void solve( ){
dp[0][1][0] = 1 ;
for( int i = 0 ; i < N ; i ++ ){
for( int j = 1 ; j <= tw - w ; j ++ ){
Node *nd = &w[j] ;
for( int k = 0 ; k <= 10 ; k ++ ){
if( !dp[i][j][k] ) continue ;
for( int sn = 0 ; sn < 4 ; sn ++ ){
Node *nxt = nd->ch[sn] ;
if( !nxt ) continue ;
if( nxt->maxLen >= k + 1 )
dp[i+1][nxt->id][0] = ( dp[i+1][nxt->id][0] + dp[i][j][k] ) %mmod ;
else if( k != 10 )
dp[i+1][nxt->id][k+1] = ( dp[i+1][nxt->id][k+1] + dp[i][j][k] ) %mmod ;
}
}
}
}
for( int j = 1 ; j <= tw - w ; j ++ )
ans = ( ans + dp
[j][0] ) %mmod ;
printf( "%d" , ans ) ;
}

int main(){
//freopen( "protect.in" , "r" , stdin ) ;
//freopen( "protect.out", "w" , stdout) ;
scanf( "%d%d" ,&N , &M ) ;
root = ++tw ; root->id = tw - w ;
for( int i = 1 ; i <= M ; i ++ ){
scanf( "%s" , ss ) ;
len = strlen( ss ) ;
for( int j = 0 ; j < len ; j ++ )
if( ss[j] == 'A' ) ss[j] = 0 ;
else if( ss[j] == 'T' ) ss[j] = 1 ;
else if( ss[j] == 'C' ) ss[j] = 2 ;
else if( ss[j] == 'G' ) ss[j] = 3 ;
Insert() ;
}
getFail( ) ;
solve() ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: