hdu 4117 GRE Words (AC自动机+线段树)

hdu 4117 GRE Words (AC自动机+线段树)

题意:给出n个字符串,每个字符串有一个权值,我们从中拿出若干个来,这若干个字符串,前一个必须是后一个的子串,问,我们能拿出的这若干个串的权值和最大是多少。

解题思路:AC自动机。字符串匹配算法,大概就是kmp,ac自动机,后缀数组,后缀自动机这么几种了。对于这题,我们很容易想到暴力dp,用kmp去匹配,总复杂度可以做到o(n^2+2*m)(n为字符串个数,m为所有字符串的总长),但这样还不够,超时妥妥的。那就要考虑怎么维护这个dp了。把自动机建好后,从前往后一个个的,去匹配。在自动机上匹配的过程中,我们可以发现,这个串走到的所有的节点,以及这些节点一路往根fail的节点,都是这个串的子串,那么以这个串结尾的最大值,就是这所有节点的最大值,加上当前枚举的串的权值了。然而,我们如果在匹配的过程中,一直fail走,那么,超时也是妥妥的,m^2的数据还是能构造出来的。因此,我们要考虑的是怎么维护这个最大值了。对于fail指针,理解深刻的话,应该很容易想到,我们根据fail指针连边,会构成一颗fail树,那么对于当前串走到的某一个节点,他的最大值,就是fail树上,从他到根的这条链上的最大值了。那么当我们得到以某一个字符串为结尾的序列,能得到的最大值时,我们能影响到哪些节点的最值呢?很显然,fail树上,该节点的子树都会受到影响,那么就好办了。一颗子树,根据dfs序,是一段连续的区间,根据这个,我们就可以构造线段树了。区间更新,单点询问最大值就ok了。

另外,这题要处理的细节还是不少的。。。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
using namespace std ;

const int maxn = 333333 ;
const int INF = -111111111 ;
int li[maxn] , num[maxn] ;
int le[maxn] , ri[maxn] ;
int col[maxn<<2] , mx[maxn<<2] , to[maxn] ;
int dp[maxn] ;

struct Edge {
    int t , next ;
} edge[maxn<<1] ;
int head[maxn] , T , cnt ;

void new_edge ( int a , int b ) {
    edge[T].t = b ;
    edge[T].next = head[a] ;
    head[a] = T ++ ;
}

void push_up ( int rt ) {
    mx[rt] = max ( mx[rt<<1] , mx[rt<<1|1] ) ;
}

void push_down ( int rt ) {
    if ( col[rt] ) {
        col[rt<<1] = max ( col[rt<<1] , col[rt] ) ;
        col[rt<<1|1] = max ( col[rt<<1|1] , col[rt] ) ;
        mx[rt<<1] = max ( mx[rt<<1] , col[rt] ) ;
        mx[rt<<1|1] = max ( mx[rt<<1|1] , col[rt] ) ;
        col[rt] = 0 ;
    }
}

void build ( int l , int r , int rt ) {
    col[rt] = mx[rt] = 0 ;
    if ( l == r ) return ;
    int m = ( l + r ) >> 1 ;
    build ( lson ) ;
    build ( rson ) ;
    push_up ( rt ) ;
}

int query ( int a , int l , int r , int rt ) {
    if ( l == r ) return mx[rt] ;
    push_down ( rt ) ;
    int m = ( l + r ) >> 1 ;
    int ret = 0 ;
    if ( a <= m ) ret = query ( a , lson ) ;
    else ret = query ( a , rson ) ;
    push_up ( rt ) ;
    return ret ;
}

void update ( int a , int b , int c , int l , int r , int rt ) {
    if ( a <= l && r <= b ) {
        col[rt] = max ( c , col[rt] ) ;
        mx[rt] = max ( c , mx[rt] ) ;
        return ;
    }
    push_down ( rt ) ;
    int m = ( l + r ) >> 1 ;
    if ( a <= m ) update ( a , b , c , lson ) ;
    if ( m < b ) update ( a , b , c , rson ) ;
    push_up ( rt ) ;
}

struct ac_auto {
    queue<int> Q ;
    int tot ;
    int c[26][maxn] , fail[maxn] , val[maxn] ;
    inline int new_node () {
        int i ;
        for ( i = 0 ; i < 26 ; i ++ )
            c[i][tot] = 0 ;
        fail[tot] = val[tot] = 0 ;
        return tot ++ ;
    }
    void insert ( char *s , int id ) {
        int len = strlen ( s ) ;
        int i , now = 0 ;
        for ( i = 0 ; i < len ; i ++ ) {
            int k = s[i] - 'a' ;
            if ( !c[k][now] ) c[k][now] = new_node () ;
            now = c[k][now] ;
        }
        to[id] = now ;
    }

    void get_fail () {
        int u = 0 , i , e , 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] ;
                    Q.push ( e ) ;
                }
                else c[i][u] = c[i][fail[u]] ;
            }
        }
    }

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

    void dfs ( int u , int fa ) {
        int i ;
        le[u] = ++ cnt ;
        for ( i = head[u] ; i != -1 ; i = edge[i].next ) {
            int v = edge[i].t ;
            if ( v == fa ) continue ;
            dfs ( v , u ) ;
        }
        ri[u] = cnt ;
    }

    void fuck () {
        int i ;
        cnt = 0 ;
        memset ( head , -1 , sizeof ( head ) ) ;
        for ( i = 1 ; i < tot ; i ++ ) {
            new_edge ( fail[i] , i ) ;
            new_edge ( i , fail[i] ) ;
        }
        dfs ( 0 , -1 ) ;
    }

    void work ( char *s , int t ) {
        int i , now = 0 ;
        int ans = 0 , mx = 0 ;
        build ( 1 , cnt , 1 ) ;
        for ( i = 0 ; i < t ; i ++ ) {
            int k = s[i] - 'a' ;
            now = c[k][now] ;
            mx = max ( mx , query ( le[now] , 1 , cnt , 1 ) ) ;
            if ( li[i] ) {
                int add = num[li[i]] > 0 ? num[li[i]] : 0 ;
                ans = max ( ans , mx + add ) ;
                update ( le[now] , ri[now] , mx + add , 1 , cnt , 1 ) ;
                now = mx = 0 ;
            }
        }
        printf ( "%d\n" , ans ) ;
    }

} ac ;

char s[maxn] ;
char s1[maxn] ;

int main () {
    int cas , ca = 0 , i ;
    scanf ( "%d" , &cas ) ;
    while ( cas -- ) {
        int n , j ;
        scanf ( "%d" , &n ) ;
        ac.init () ;
        int t = 0 ;
        T = 0 ;
        memset ( li , 0 , sizeof ( li ) ) ;
        for ( i = 1 ; i <= n ; i ++ ) {
            scanf ( "%s%d" , s , &num[i] ) ;
            ac.insert ( s , i ) ;
            int len = strlen ( s ) ;
            for ( j = 0 ; j < len ; j ++ )
                s1[t++] = s[j] ;
            li[t-1] = i ;
        }
        ac.get_fail () ;
        ac.fuck () ;
        printf ( "Case #%d: " , ++ ca ) ;
        ac.work ( s1 , t ) ;
    }
    return 0 ;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值