前置芝士:\(Trie\)字典树

这道题,说是AC自动机,实际上一个\(Trie+\)队列轻松搞定。

首先,我们对所有单词建一棵\(Trie\)。

然后,定义一个空队列\(Q\),初始时把\(-1\)放进去(因为字符串下标从\(0\)开始,待会儿详细叙述原因)。

接着,对于每一篇询问的文章\(T\),进行如下操作:

  1. 取出队头元素,假设是\(x\)。
  2. 更新\(ans=\max(ans,x)\)。
  3. 从\(T[x+1]\)开始枚举,进行\(Trie\)上的匹配。(这也解释了为什么刚开始要把\(-1\)放进去,因为这样才能从\(0\)开始枚举)
  4. 如果成功匹配\(T[i]\),继续枚举,直到第\(5\)步被执行或者\(i\ge T.length\)。
  5. 否则,如果发现匹配不了了,立即退出循环,跳回第\(1\)步。
  6. 如果成功匹配\(T[i]\),并且发现这里有字符串结尾标记,则说明成功匹配了一个单词,把\(i\)放进队尾。(注意:此时不能立即退出,待会儿讲原因)
  7. 执行\(1-6\)步,直到队列为空。

说明一下第\(6\)步,此时为什么不能直接退出呢?

比如:词典为\(\{what,whatis\}\),文章为\(whatisbalabala\)。

如果直接退出,则匹配到\(i=3\)时就退出了,最后输出答案为\(4\)。(而实际为\(6\))

这样,就可以开心地\(code\)啦:(看完代码不要心急,继续往下看)

#include <bits/stdc++.h>
using namespace std;
int n,m,trie[1000005][26],tot,c[1000005];
char tmp[1000005];
queue<int> q;
inline void addstring(char a[]){//添加字符串
int len=strlen(a),pos=0;
for(int i=0;i<len;i++){
if(!trie[pos][a[i]-'a']){
trie[pos][a[i]-'a']=++tot;
pos=trie[pos][a[i]-'a'];
}
else pos=trie[pos][a[i]-'a'];
}
c[pos]=true;
}
inline int find(char a[]){
memset(flag,0,sizeof(flag));
int len=strlen(a),pos=0,ans=-1;q.push(-1);
while(!q.empty()){
int x=q.front();q.pop();//步骤1
ans=max(ans,x);pos=0;//步骤2
for(int i=x+1;i<len;i++){//步骤3
if(trie[pos][a[i]-'a']) pos=trie[pos][a[i]-'a'];//步骤4
else break;//步骤5
if(c[pos]) q.push(i);//步骤6
}
}
return ans==-1?0:ans+1;//字符串下标以0开始,而题目中以1开始
}
int main(){
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;i++){
scanf("%s",tmp);addstring(tmp);
}
for(register int i=1;i<=m;i++){
scanf("%s",tmp);printf("%d\n",find(tmp));
}
return 0;
}

开心的交上去,咦?怎么只有\(73pts\)?

经不懈思考,终于构造出能卡掉的数据:

字典:\(\{a,aa,aaa,...,aaaaaaaaaa\}\)

文章:\(\underbrace {aaa...aaa}_{10^6个a}\)

于是,对于几乎每个位置\(x\),都被插入队列至少\(10\)次,速度也就呵呵了......

那么,如何防止一个位置被重复插入?很简单,做个标记就行了。

改进后的代码:\((AC)\)

#include <bits/stdc++.h>
using namespace std;
int n,m,trie[1000005][26],tot,c[1000005],flag[1000005];//flag即为标记数组
char tmp[1000005];
queue<int> q;
inline void addstring(char a[]){
int len=strlen(a),pos=0;
for(int i=0;i<len;i++){
if(!trie[pos][a[i]-'a']){
trie[pos][a[i]-'a']=++tot;
pos=trie[pos][a[i]-'a'];
}
else pos=trie[pos][a[i]-'a'];
}
c[pos]=true;
}
inline int find(char a[]){
memset(flag,0,sizeof(flag));//初始化标记数组
int len=strlen(a),pos=0,ans=-1;q.push(-1);
while(!q.empty()){
int x=q.front();q.pop();
ans=max(ans,x);pos=0;
if(flag[x]) continue;//判断一下该位置是否已经有标记了,如果有就continue
if(x!=-1) flag[x]=1;//否则做个标记
for(int i=x+1;i<len;i++){
if(trie[pos][a[i]-'a']) pos=trie[pos][a[i]-'a'];
else break;
if(c[pos]) q.push(i);
}
}
return ans==-1?0:ans+1;
}
int main(){
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;i++){
scanf("%s",tmp);addstring(tmp);
}
for(register int i=1;i<=m;i++){
scanf("%s",tmp);printf("%d\n",find(tmp));
}
return 0;
}//开心的结束

最后,蒟蒻写博客不易,恳请大佬点个赞!

【Luogu】P2292 [HNOI2004]L语言 题解的更多相关文章

  1. Luogu P2292 [HNOI2004]L语言(Trie+dp)

    P2292 [HNOI2004]L语言 题面 题目描述 标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的.现在你要处理的就是一段没有标点的文章. 一段文章 \(T\) 是由若干小写字母构成. ...

  2. Luogu P2292 [HNOI2004]L语言

    题目链接 \(Click\) \(Here\) 好久没写\(DP\)了真是水平下降不少,一眼把这个题搞成贪心了,然后一发交上只有\(37\)分\(QwQ\) 这个题好像还可以\(AC\)自动机胡搞?不 ...

  3. 洛谷:P2292 [HNOI2004]L语言(DP+Trie树)

    P2292 [HNOI2004]L语言 题目链接:https://www.luogu.org/problemnew/show/P2292 题目描述 标点符号的出现晚于文字的出现,所以以前的语言都是没有 ...

  4. 2021.11.09 P2292 [HNOI2004]L语言(trie树+AC自动机)

    2021.11.09 P2292 [HNOI2004]L语言(trie树+AC自动机) https://www.luogu.com.cn/problem/P2292 题意: 标点符号的出现晚于文字的出 ...

  5. 洛谷 P2292 [HNOI2004] L语言 解题报告

    P2292 [HNOI2004] L语言 题目描述 标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的.现在你要处理的就是一段没有标点的文章. 一段文章\(T\)是由若干小写字母构成.一个单词 ...

  6. 洛谷(cogs 1293/bzoj 1212) P2292 [HNOI2004]L语言

    1293. [HNOI2004] L语言 ★★★   输入文件:language.in   输出文件:language.out   简单对比时间限制:1 s   内存限制:162 MB [题目描述] ...

  7. P2292 [HNOI2004]L语言

    传送门 思路:  毒瘤的字典树! ▲主要分有两个步骤: ① 日常的建树. ② 暴力地求解. ▲日常建树:过于基础,跳过. ▲重点在于如何暴力地求解而不被卡掉(DP?不存在的) 可以利用区间动规的思想, ...

  8. 洛谷P2292 [HNOI2004]L语言

    传送门 建好trie树 当$dp[j]==1$当且仅当存在$dp[k]=1$且$T[k+1,j]==word[i]$ 然后乱搞就行了 //minamoto #include<iostream&g ...

  9. 洛谷 P2292 [HNOI2004]L语言

    题目描述 标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的.现在你要处理的就是一段没有标点的文章. 一段文章T是由若干小写字母构成.一个单词W也是由若干小写字母构成.一个字典D是若干个单词的 ...

随机推荐

  1. 085 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 04 构造方法调用

    085 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 04 构造方法调用 本文知识点:构造方法调用 说明:因为时间紧张,本人写博客过程中只是 ...

  2. matlab中的polyfit函数。

    来源:https://blog.csdn.net/zhaluo0051/article/details/77949170 :https://blog.csdn.net/g28_gwf/article/ ...

  3. PADS Layout VX.2.3 将PCB中的元器件封装保存到库

    工具1:PADS Layout VX.2.3 菜单File > Library...,打开Library Manager,点击Create New Lib...新建一个库. 使用快捷键Ctrl ...

  4. 《流畅的Python》第二部分 数据结构 【序列构成的数组】【字典和集合】【文本和字节序列】

    第二部分 数据结构 第2章 序列构成的数组 内置序列类型 序列类型 序列 特点 容器序列 list.tuple.collections.deque - 能存放不同类型的数据:- 存放的是任意类型的对象 ...

  5. 第十四周C++学习总结

    类模板使用方法:类模板名 <数据类型> 对象名: C++有个标准模板库(STL)(standard template library),编程时使用它会提高程序的可靠性. Stl 包含了(容 ...

  6. CMD/ENTROYPOINT区别

    CMD/ENTROYPOINT区别 相同点:都是指定一个容器:启动时要运行的命令 不同点(重点): CMD: dockerfile中可以有多个CMD指令,但是只有最后一个生效,CMD会被docker ...

  7. 多测师_肖sir_git _004(版本控制器)

    gitgit 是一个开源的分布式版本控制系统,用于敏捷高效的处理任何大小的项目.git是linux torvalds 为了帮助管理linux内核开发的一个开放源码的版本控制软件.git与常用的版本控制 ...

  8. pytest文档56-插件打包上传到 pypi 库

    前言 pytest 的插件完成之后,可以上传到 github,方便其他小伙伴通过 pip 源码安装.如果我们想通过 pip install packages 这种方式安装的话,需上传到 pypi 仓库 ...

  9. RDS 事务型数据库sql

    -- 替换json中数据 select SUBSTRING_INDEX(SUBSTRING_INDEX('[{"channelCode":"MOBIL",&qu ...

  10. BASH让标准输出和错误输出颜色不同

    shell中运行的程序输出有标准输出(stdout)和错误输出(stderr)两种.当在终端中运行一个进程时,默认是stdout和stderr混在一起的,需要区分只能去读内容,人眼不容易快速区分. 如 ...