BZOJ1212 [HNOI2004]L语言【Trie】

Time Limit: 10 Sec Memory Limit: 162 MB

Description

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

Input

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

Output

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

Sample Input
4 3
is
name
what
your
whatisyourname
whatisyouname
whaisyourname

Sample Output

14
6
0


题目分析:

整体思路:Trie树+ 暴力 DP

用dp[i]表示文章中长度为i的前缀是否能被理解
将每个单词插入Trie树
并将所有单词长度记录于length数组并升序排序

对于每个文章,初始时dp[0]=true
从文章的第length[1]-1位开始匹配 (即前面留出至少一个单词的空位)

假设当前匹配的位置为i
对于所有length[j]( 1< j < n)
dp[i-length[j]+1]==true
那么就查询从文章中i-length[j]+1 到 i位的字符串是否在Trie中出现过
出现过则更新答案

匹配中遇到i-length[j]+1<0 则说明单词长度超过了当前匹配的位置
由于排了序,接下来的单词也都超出长度,所以直接跳出循环


#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

struct node
{
    node* nxt[26];
    int judge;
    node(){for(int i=0;i<26;++i)nxt[i]=NULL; judge=0;}
};
node* rt=new node();
int n,m;
char pt[20],txt[1000010];
int length[50];
int dp[1000010];

void ins(char *ss,int len)
{
    node *p=rt;
    for(int i=0;i<len;++i)
    {
        int x=ss[i]-'a';
        if(p->nxt[x]==NULL) { node* k=new node(); p->nxt[x]=k;}
        p=p->nxt[x]; 
    }
    p->judge=1;
}

int find(int ll,int rr)
{
    node* p=rt;
    for(int i=ll;i<=rr;++i)
    {
        int x=txt[i]-'a';
        p=p->nxt[x];
        if(p==NULL) return 0;
    }
    if(p->judge) return 1;
    else return 0;
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
    {
        scanf("%s",&pt);
        int len=strlen(pt);
        ins(pt,len);//插入Trie
        length[i]=len;
    }

    sort(length+1,length+1+n);//将单词长度升序排序
    while(m--)
    {
        scanf("%s",&txt);
        memset(dp,0,sizeof(dp)); dp[0]=1;//记得初始化

        int len=strlen(txt),ans=0;
        for(int i=length[1]-1;i<len;++i)//要从length[1]-1位开始
        {
            for(int j=1;j<=n;++j)
            {
                int num=i-length[j]+1;
                if(num<0) break;//单词长度超出匹配范围直接退出
                if(!dp[num]||length[j]==length[j-1])continue;
                //相同长度不再匹配
                if(find(num,i)) dp[i+1]=1,ans=i+1;//匹配成功则更新
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/niiick/article/details/80199375
今日推荐