[BZOJ1212]-[HNOI2004]L语言-trie+DP

说在前面

本来以为要建AC自动机,结果发现网上的题解都…直接上trie就可以了= =???
复杂度明明爆炸啊…


题目

BZOJ1212传送门

题面

一段文章T是由若干小写字母构成。一个单词W也是由若干小写字母构成。一个字典D是若干个单词的集合。 我们称一段文章T在某个字典D下是可以被理解的,是指如果文章T可以被分成若干部分,且每一个部分都是字典D中的单词。 例如字典D中包括单词{is, name, what, your},则文章whatisyourname是在字典D下可以被理解的,因为它可以分成4个单词:what, is, your, name,且每个单词都属于字典D。同理,文章‘whatisyouname’ 在字典D下不能被理解,不过这段文章的一个前缀whatis是可以被理解的。
现在给定一个字典D,你的程序需要判断若干段文章在字典D下是否能够被理解。 并给出其在字典D下能够被理解的最长前缀的位置。

输入输出格式

输入格式:
输入文件第一行是两个正整数n和m,表示字典D中有n个单词,且有m段文章需要被处理。 之后的n行每行描述一个单词,再之后的m行每行描述一段文章。 其中1<=n, m<=20,每个单词长度不超过10,每段文章长度不超过1e6

输出格式:
对于输入的每一段文章,你需要输出这段文章在字典D可以被理解的最长前缀的位置


解法

建好trie直接跑dp
令( bool ) dp[i]表示可以理解长度为i的前缀
如果当前dp[i]为true,就进trie去匹配,匹配到一个末尾节点就更新dp,刷表法


下面是自带大常数的代码

/**************************************************************
    Problem: 1212
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:516 ms
    Memory:2872 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
 
int N , M , lena , maxlen , ans ;
bool dp[1024*1024+5] ;
char ss[25][25] , a[1024*1024+5] ;
struct Node{
    bool isend ;
    char c ;
    Node *ch[26] ;
}*root ;
 
void newNode( Node *&nd ){
    nd = new Node() ;
    nd->isend = false ;
    memset( nd->ch , 0 , sizeof( nd->ch ) ) ;
}
 
void Insert( char *ts ){
    int len = strlen( ts ) ;
    maxlen = max( maxlen , len ) ;
    Node *nd = root ;
    for( int i = 0 ; i < len ; i ++ ){
        int nxt = ts[i] - 'a' ;
        if( !nd->ch[nxt] ){
            newNode( nd->ch[nxt] ) ;
            nd->ch[nxt]->c = ts[i] ;
        }
        nd = nd->ch[nxt] ;
    }
    nd->isend = true ;
}
 
inline void update( int pos ){
    Node *nd = root ;
    for( ; ; ){
        //printf( "address(%d-[%c]) pos(%d) \n" , nd , nd->c , pos ) ;
        int nxt = a[pos] - 'a' ;
        if( !nd->ch[nxt] ) return ;
        nd = nd->ch[nxt] ; pos ++ ;
        if( nd->isend ) dp[pos] = true ;
    }
}
 
void solve(){
    for( register int i = 1 , j , tmp  ; i <= M ; ++ i ){
        scanf( "%s" , a ) ; lena = strlen( a ) ;
        dp[0] = 1 ; ans = 0 ;
        for( j = 0 , tmp = 0 ; j < lena ; ++ j ){
            if( dp[j] ){
                update( j ) ;
                tmp = 0 ; ans = j ;
            } else {
                ++ tmp ;
                if( tmp == maxlen ) break ;
            }
        }
        printf( "%d\n" , dp[lena] ? lena : ans ) ;
        memset( dp , 0 , ( lena + 1 ) * sizeof( bool ) ) ;
    }
}
 
int main(){
    newNode( root ) ; root->c = 0 ;
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 ; i <= N ; i ++ ){
        scanf( "%s" , ss[i] ) ;
        Insert( ss[i] ) ;       
    }
    solve() ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值