BZOJ1212: [HNOI2004]L语言
【传送门:BZOJ1212】
简要题意:
给出n个单词,m个母串,输出每个母串最多能用单词覆盖的最长前缀长度(单词与单词之间不可重叠)
题解:
一开始想着用AC自动机,结果发现fail指针一点用也没有
就直接建字典树,v[i]表示母串1到i能够被单词覆盖,true表示能覆盖,一开始v[0]=true,然后对于一个母串,从头到尾扫一遍,假设当前扫到i点并且i点能够被单词覆盖,则将i+1到len在字典树中跑,加入能跑到一个单词的结尾,v[k]=true(k表示这个单词的结尾位于母串的位置)
最后从len往前枚举,找到第一个v为true时,输出当前位置即可
数据竟然有重复的单词出现,害WA了一次
参考代码:
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; struct trie { int s,c[27],fail,d; trie() { s=fail=d=0; memset(c,-1,sizeof(c)); } }t[210];int cnt; char a[210],st[1100000]; int v[1100000]; void bt() { int x=0,len=strlen(a+1); for(int i=1;i<=len;i++) { int y=a[i]-'a'+1; if(t[x].c[y]==-1) t[x].c[y]=++cnt; x=t[x].c[y]; } t[x].s++; } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",a+1); bt(); } memset(v,0,sizeof(v)); for(int i=1;i<=m;i++) { scanf("%s",st+1); int len=strlen(st+1); int x=0,ans=0; v[0]=i; while(x<=len) { while(v[x]!=i) { x++; if(x==len+1) break; } int j=0; for(int k=x+1;k<=len;k++) { int y=st[k]-'a'+1; if(t[j].c[y]==-1) break; else { j=t[j].c[y]; if(t[j].s>0) v[k]=i; } } x++; } for(int j=len;j>=1;j--) if(v[j]==i){ans=j;break;} printf("%d\n",ans); } return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚