zoj 3535 Gao the String II(ac自动机+dp)

Gao the String II
题意:有字符串集合A,B。每个集合内最多有50个字符串,每个字符串长度不超过10,用A集合内的字符串link(link操作就是连接,比如已经link得到一个s,那么可以直接在s后面接上一个A里面的字符串,也可以将s的后缀跟要接上的串的前缀叠在一起连接)成一个长度不超过L的字符串s,问s与每一个B里面的字符串匹配,得到的匹配位置数之和最大是多少?ps:比赛的时候没注意到时不超过L啊,还以为一定要L,晕死。。。
解题思路:如果我们得到了所有的s,那么跟B去匹配就完事了。但是,很显然,s的可能情况太多。建立B的ac自动机(为什么想到自动机?不知道,反正就是想到了),然后定义dp状态,dp[i][j][k]表示s的长度为i,以A中的第j个字符串为结尾,匹配到自动机上的k节点时,最大的匹配点数是多少。要预处理很多东西,首先要预处理出,A集合里的字符串两两匹配的所有能link的位置,然后还要预处理从自动机的t1节点,接上一个A里面的每一个字符串的每一个后缀能增加多少个匹配点,接上之后,会匹配到那个节点上。这些弄好了,dp递推的复杂度才会合适。
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std ;

const int N = 51 ;
const int maxn = 501 ;
int dp[N][N][maxn] , inc[N][N][N] ;
vector<int> vec[N][N] ;
short add[12][N][maxn] , to[12][N][maxn] ;
char s1[N][N] , s2[N][N] ;
int n , m , l ;
int len[N] ;

struct AC_auto {
    int c[26][maxn] , tot , cnt[maxn] , fail[maxn] ;
    queue<int> Q ;
    int new_node () {
        int i ;
        for ( i = 0 ; i < 26 ; i ++ ) c[i][tot] = 0 ;
        fail[tot] = cnt[tot] = 0 ;
        return tot ++ ;
    }

    void insert ( char *s ) {
        int now = 0 ;
        for ( ; *s ; s ++ ) {
            int k = *s - 'a' ;
            if ( !c[k][now] ) c[k][now] = new_node () ;
            now = c[k][now] ;
        }
        cnt[now] ++ ;
    }

    void get_fail () {
        int u = 0 , e , i , j ;
        for ( i = 0 ; i < 26 ; i ++ )
            if ( c[i][u] ) Q.push ( c[i][u] ) ;
        while ( !Q.empty () ) {
            u = Q.front () ;
            Q.pop () ;
            for ( i = 0 ; i < 26 ; i ++ ) {
                if ( c[i][u] ) {
                    e = c[i][u] ;
                    j = fail[u] ;
                    fail[e] = c[i][j] ;
                    cnt[e] += cnt[fail[e]] ;
                    Q.push ( e ) ;
                }
                else c[i][u] = c[i][fail[u]] ;
            }
        }
    }

    void work () {
        int i , j , k , t , now , s , v , x , p , q ;
        for ( i = 0 ; i <= 11 ; i ++ )
            for ( k = 1 ; k <= n ; k ++ )
                for ( t = 0 ; t < tot ; t ++ )
                    add[i][k][t] = 0 ;
        for ( i = 1 ; i <= n ; i ++ )
            for ( k = 0 ; k < tot ; k ++ ) {
                for ( v = 1 ; v <= len[i] ; v ++ ) {
                    now = k ;
                    for ( x = v ; x <= len[i] ; x ++ ) {
                        p = s1[i][x] - 'a' ;
                        now = c[p][now] ;
                        add[v][i][k] += cnt[now] ;
                    }
                    to[v][i][k] = now ;
                }
            }
        for ( i = 0 ; i <= l ; i ++ )
            for ( j = 1 ; j <= n ;j ++ )
                for ( k = 0 ; k < tot ; k ++ )
                    dp[i][j][k] = -1111111 ;
        for ( i = 1 ; i <= n ; i ++ ) {
            if ( len[i] > l ) continue ;
            now = 0 ;
            x = 0 ;
            for ( j = 1 ; j <= len[i] ; j ++ ) {
                k = s1[i][j] - 'a' ;
                now = c[k][now] ;
                x += cnt[now] ;
            }
            dp[len[i]][i][now] = x ;
        }

        for ( i = 0 ; i < l ; i ++ )
            for ( j = 1 ; j <= n ; j ++ )
                for ( k = 0 ; k < tot ; k ++ ) {
                    if ( dp[i][j][k] == -1111111 ) continue ;
                    for ( t = 1 ; t <= n ; t ++ ) {
                        s = vec[j][t].size () ;
                        for ( x = 0 ; x < s ; x ++ ) {
                            v = vec[j][t][x] ;
                            p = i + inc[j][t][v] ;
                            if ( p > l ) continue ;
                            q = to[v][t][k] ;
                            dp[p][t][q] = max ( dp[p][t][q] , dp[i][j][k] + add[v][t][k] ) ;
                        }
                    }
                }

        int ans = 0 ;
        for ( i = 1 ; i <= l ; i ++ )
            for ( j = 1 ; j <= n ; j ++ )
                for ( k = 0 ; k < tot ; k ++ )
                    ans = max ( ans , dp[i][j][k] ) ;
        printf ( "%d\n" , ans ) ;
    }

    void init () {
        tot = 0 ;
        new_node () ;
    }

} ac ;

void init () {
    ac.init () ;
    int i , j , k , t , l1 , l2 , flag ;
    for ( i = 1 ; i <= m ; i ++ )
        ac.insert ( s2[i] + 1 ) ;
    ac.get_fail () ;
    for ( i = 1 ; i <= n ; i ++ )
        len[i] = strlen( s1[i] + 1 ) ;
    for ( i = 1 ; i <= n ; i ++ )
        for ( j = 1 ; j <= n ; j ++ ) {
            vec[i][j].clear () ;
            l1 = len[i] ;
            l2 = len[j] ;
            vec[i][j].push_back ( 1 ) ;
            inc[i][j][1] = l2 ;
            for ( k = l1 ; k >= 2 ; k -- ) {
                if ( l1 - k + 1 >= l2 ) break ;
                flag = 1 ;
                for ( t = k ; t <= l1 ; t ++ )
                    if ( s1[i][t] != s1[j][t-k+1] ) {
                        flag = 0 ;
                        break ;
                    }
                if ( flag ) {
                    vec[i][j].push_back ( l1 - k + 2 ) ;
                    inc[i][j][l1-k+2] = l2 - l1 + k - 1 ;
                }
            }
        }
    ac.work () ;
}

int main () {
    int i , j , k ;
    while ( scanf ( "%d%d%d" , &n , &m , &l ) != EOF ) {
        for ( i = 1 ; i <= n ; i ++ )
            scanf ( "%s" , s1[i] + 1 ) ;
        for ( i = 1 ; i <= m ; i ++ )
            scanf ( "%s" , s2[i] + 1 ) ;
        init () ;
    }
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值