【bzoj1212】【HNOI2004】L语言
题目描述
标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。
一段文章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下能够被理解的最长前缀的位置。
输入
输入文件第一行是两个正整数n和m,表示字典D中有n个单词,且有m段文章需要被处理。之后的n行每行描述一个单词,再之后的m行每行描述一段文章。
其中1<=n, m<=20,每个单词长度不超过10,每段文章长度不超过1M。
输出
对于输入的每一段文章,你需要输出这段文章在字典D可以被理解的最长前缀的位置。
样例输入
4 3 is name what your whatisyourname whatisyouname whaisyourname
样例输出
14 6 0
题解
trie+dp。dp[ i ] 表示位置 i 之前的前缀能不能被理解,那么 dp[ i ] = 1的条件为 存在 j ≤ i && dp[ j ] =1 && s[ j ] 到 s [ i ] 能被理解。
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=1000+50; int n,m,t[maxn][30],len,tot,root; char c[1000000+50]; bool word[maxn],dp[1000000+50]; void insert(){ len=strlen(c+1); root=0;int id; for(int i=1;i<=len;i++){ id=c[i]-'a'; if(!t[root][id]) t[root][id]=++tot; root=t[root][id]; } word[root]=true; } void find(){ len=strlen(c+1); memset(dp,0,sizeof(dp)); dp[0]=1; root=0;int id; for(int i=0;i<len;i++){ if(dp[i]){ int j=i+1; root=t[0][c[j]-'a']; while(root&&j<=len){ if(word[root]) dp[j]=1; root=t[root][c[++j]-'a']; } } } for(int i=len;i>=1;i--){ if(dp[i]){ printf("%d\n",i); return ; } } printf("0\n"); } template<typename T>void read(T& aa){ char cc; ll ff;aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int main(){ memset(word,false,sizeof(word)); read(n),read(m); for(int i=1;i<=n;i++){ cin>>c+1; insert(); } while(m--){ cin>>c+1; find(); } return 0; }