[BZOJ1212][HNOI2004]L语言:AC自动机+DP

分析:

注意到题目要求单词不重叠恰好覆盖文章,所以不能直接上AC自动机。
这里的思路有点像可行性DP,f[i]表示是/否可以用一些单词恰好覆盖前i个字符,显然f[i]=f[i] or f[i-一个可行的单词],这个DP过程可以用AC自动机优化。
具体见代码。
(AC自动机的根的编号设为0更好写,好长时间没写AC自动机了这道题调了好长时间 。)

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <ctime>
//#include <unistd.h>

int n,m,tot=0,tr[205][26],fail[205],l[205];
char s[1100005];
bool ed[205],f[1100005];

inline void ins(char* str){
    int now=0,len=strlen(str+1);
    for(int i=1;i<=len;i++){
        if(!tr[now][str[i]-'a']) tr[now][str[i]-'a']=++tot;
        now=tr[now][str[i]-'a'];
    }
    ed[now]=1;
    l[now]=len;
}

std::queue<int> q;
inline void getfail(){
    while(!q.empty()) q.pop();
    for(int i=0;i<26;i++){
        if(!tr[0][i]) continue;
        fail[tr[0][i]]=0;
        q.push(tr[0][i]);
    }
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[u][i]){
                fail[tr[u][i]]=tr[fail[u]][i];
                q.push(tr[u][i]);
            }
            else tr[u][i]=tr[fail[u]][i];
        }
        ed[u]|=ed[fail[u]];
    }
}

int query(char* str){
    int now=0,len=strlen(str+1);
    for(int i=1;i<=len;i++){
        now=tr[now][str[i]-'a'];
        if(ed[now]){
            int temp=now;
            while(temp){
                if(l[temp]) f[i]|=f[i-l[temp]];
                temp=fail[temp];
            }
        }
    }
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        getchar();
        scanf("%s",s+1);
        ins(s);
    }
    getfail();
    for(int i=1;i<=m;i++){
        getchar();
        scanf("%s",s+1);
        memset(f,0,sizeof f);
        f[0]=1;
        query(s);
        int len=strlen(s+1);
        for(int i=len;i+1;i--){
            if(f[i]){
                printf("%d\n",i);
                break;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9724244.html