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

说在前面

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[N][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() ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值