BZOJ 1212 && Luogu 2292 [HNOI 2004] L语言

BZOJ题面

Luogu题面

刚学 Trie 碰到这题差点懵逼

表示加了个 DP 我差点就不会了

 

主要讲一下 DP 的思路

F[i] == 1 表示以第 i 个字符(从 0 开始)结尾的字符串能被理解

同理,F[i] == 0 表示不能理解

当满足 F[j] == 1 且以 F[j+1] 开头,以 F[i] 结尾的单词在 Trie 内

把 F[i] 置为 1

最后只要输出一个最大的 i 使得 F[i] == 1 即可

蒟蒻专属暴力思路,好想又好写,但奇慢无比,在 Luogu 上第十个点 TLE,幸亏有 O2 这种神奇的东西

 

代码

#include<bits/stdc++.h>
using namespace std;
int cnt,f[1050000];
char a[11],s0[1050000];
queue<int>q;
struct tree{  // Trie
    int fail,end,vis[26];
}ac[220];
inline void build(char s[]){  // 建 Trie
    int l=strlen(s),now=0;
    for(int i=0;i<l;i++){
        int ch=s[i]-'a';  // 优化:少算 2 次减法
        if(!ac[now].vis[ch]) ac[now].vis[ch]=++cnt;
        now=ac[now].vis[ch];
    }
    ac[now].end=1;  // 标记单词末尾
}
inline bool query(int l,int r){  // 查询以 l 和 r 为两端的单词是否在 Trie 内
    int u=0;
    for(int i=l;i<=r;i++){
        int ch=s0[i]-'a';
        if(!ac[u].vis[ch]) return 0;  // 节点不存在则返回 False
        u=ac[u].vis[ch];
    }
    return ac[u].end;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    while(n--){
        scanf("%s",a);
        build(a);
    }
    while(m--){
        scanf("%s",s0);
        int l=strlen(s0),ans=0;
        for(int i=0;i<l;i++) f[i]=0;
        for(int i=0;i<l;i++){
            for(int j=max(i-10,-1);j<=i;j++)  // 单词长度最大为 10,所以 DP 前 10 个字符
                if((j==-1||f[j])&&query(j+1,i)){  // 字符串下标从 0 开始,所以把 DP 最小值设为 -1
                    f[i]=1;
                    ans=i+1;  // 下标为 i 的字符是第 i+1 个
                    break;
                }
                if(i-9>ans) break;  // 长于单词长度后立即跳出
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

By The_Seventh

2017-12-29  08:06:39

posted @ 2017-12-29 08:18  The_Seventh  阅读(135)  评论(0编辑  收藏  举报