点我
题目描述
在生物学中,一些生物的结构是用包含其要素的大写字母序列来表示的。生物学家对于把长的序列分解成较短的序列(即元素)很感兴趣。
如果一个集合 P 中的元素可以通过串联(元素可以重复使用,相当于 Pascal 中的 “+” 运算符)组成一个序列 S ,那么我们认为序列 S 可以分解为 P 中的元素。元素不一定要全部出现(如下例中BBC就没有出现)。举个例子,序列 ABABACABAAB 可以分解为下面集合中的元素:
{A, AB, BA, CA, BBC}
序列 S 的前面 K 个字符称作 S 中长度为 K 的前缀。设计一个程序,输入一个元素集合以及一个大写字母序列 S ,设S'是序列S的最长前缀,使其可以分解为给出的集合P中的元素,求S'的长度K。
输入输出格式
输入格式:输入数据的开头包括 1..200 个元素(长度为 1..10 )组成的集合,用连续的以空格分开的字符串表示。字母全部是大写,数据可能不止一行。元素集合结束的标志是一个只包含一个 “.” 的行。集合中的元素没有重复。接着是大写字母序列 S ,长度为 1..200,000 ,用一行或者多行的字符串来表示,每行不超过 76 个字符。换行符并不是序列 S 的一部分。
输出格式:只有一行,输出一个整数,表示 S 符合条件的前缀的最大长度。
输入输出样例
输入样例#1:
A AB BA CA BBC . ABABACABAABC
输出样例#1:
11
说明
翻译来自NOCOW
USACO 2.3
代码
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<ctime>
#define For(i,a,b) for(register int i=a;i<=b;++i)
#define Rep(i,a,b) for(register int i=a;i>=b;--i)
const int maxx=1e5+7;
using namespace std;
int be[2000001],ne[2000001],to[2000001],e=0,cnt=0;
void add(int x, int y){ //链式前向星存trie
to[++e]=y;
ne[e]=be[x];
be[x]=e;
}
struct node{
bool end; char x;
}llz[2000001];
bool f[2000001];
char x[1200],s[2000010];
void insert(char *s){ //trie的插入
int i,u=0,len=strlen(s);
For(v,0,len-1){
bool flag=0;
for(i=be[u];i;i=ne[i]){
int go=to[i];
if(llz[go].x==s[v]){
u=go;
flag=1;
break;
}
}
if(!flag) {add(u,++cnt); u=cnt; llz[u].x=s[v];} //
if(v==len-1) llz[u].end=1; //标记结束位置
}
}
bool find(int l,int r){ //trie的查询
int i,u=0,flag;
For(v,l,r){
flag=0;
for(i=be[u];i;i=ne[i]){
int go=to[i];
if(llz[go].x==s[v]){
u=go;
flag=1;
break;
}
}
if(!flag)return 0;
if(v==r)
if(llz[u].end==1){
return 1;
}
else return 0;
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("input.in", "r", stdin);
freopen("output.out", "w", stdout);
#endif
int i,j,l,ans=0,M=0;
while(1){ //对每个单词集合建trie
scanf("%s",x);
if(x[0]=='.') break;
i=strlen(x);
M=max(i,M); //找到最长的长度,下面会有用的
insert(x);
}
scanf("%s",s+1);
while(scanf("%s",x)!=EOF) strcat(s+1,x); //读入s串
l=strlen(s+1);
f[0]=1; //f[x]表示在x位置之前所有的字母能匹配
For(i,1,l){
for(j=i-1;j>=0;j--)
if(f[j] && find(j+1,i)){ //如果j以前能全部匹配,并且j+1到i能匹配,i位置也能全匹配
f[i]=1;
ans=i;
break;
}
if(j<0 && i-ans>M) break; //剪枝,如果当前比最长集合还要长的位置没匹配成功,后面也不能匹配上
}
printf("%d\n",ans);
return 0;
}
code by 罗旅洲