Luogu P2292 [HNOI2004]L语言
Luogu P2292 [HNOI2004]L语言
解析
-
看到单词和句子匹配,再看数据范围1M的字符串(长度大约 106 级别),所以用Trie树来处理
-
句子是没有标点符号的,所以需要我们自己断句,那么我们先将所有单词加入Trie树,然后让句子在树上匹配,匹配时可不可以匹配完一个单词就将其从句子中删掉呢?
-
上面这个问题的回答是:NO,因为上面这个想法是一种贪心的思想,有可能会出现单词重叠后由于截取不当就不能继续向下匹配了,其实原本换种截取方法还是可以继续匹配的。举个例子:
3 1
abaa
abaaa
aaabba
abaaaabaaabaaaaaba上面这个样例若用贪心的方法删完一个单词后就不能再继续匹配的情况,其实整个句子都可以匹配完
-
那应该怎么做呢?答:两种方法 ------ 类dp 和 记忆化搜索
方法一:类dp
思路就是把句子上从上一个能划分的位置开始往后能连续划分单词的位置打上标记,最开始的位置是0,然后根据标记进行转移,找到最远能到达的位置即为答案
Code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int n,m,rot,trie[205][26];
char word[15],sent[1500005];
bool book[205],vis[1500005];
void insert(char s[])
{
int u=0,len=strlen(s+1);
for(int i=1;i<=len;i++)
{
int v=s[i]-'a';
if(!trie[u][v]) trie[u][v]=++rot;
u=trie[u][v];
}
book[u]=1;
return;
}
int find(char s[])
{
int u=0,l=0,len=strlen(s+1);
memset(vis,0,sizeof(vis));
for(int i=1;i<=len;i++)
{
int v=s[i]-'a';
if(!trie[u][v]) break;
u=trie[u][v];
if(book[u]) vis[i]=1;
}
for(int i=1;i<=len;i++)
{
if(!vis[i]) continue;
else l=i;
int u=0;
for(int j=i+1;j<=len;j++)
{
int v=s[j]-'a';
if(!trie[u][v]) break;
u=trie[u][v];
if(book[u]) vis[j]=1;
}
}
return l;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
cin>>(word+1);
insert(word);
}
for(int i=1;i<=m;i++)
{
cin>>(sent+1);
printf("%d\n",find(sent));
}
return 0;
}
方法二:记忆化搜索
思路与方法一有相似之处,搜索能划分单词的位置并打上标记,每次用到达的位置更新答案即可
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int n,m,l,rot,trie[205][26];
char word[15],sent[1500005];
bool book[205],vis[1500005];
void insert(char s[])
{
int u=0,len=strlen(s+1);
for(int i=1;i<=len;i++)
{
int v=s[i]-'a';
if(!trie[u][v]) trie[u][v]=++rot;
u=trie[u][v];
}
book[u]=1;
return;
}
void dfs(char s[],int x,int len)
{
if(vis[x]) return;
vis[x]=1;
int u=0,i=x;
l=max(l,i-1);
while(i<=len)
{
int v=s[i]-'a';
if(!trie[u][v]) break;
u=trie[u][v];
i++;
if(book[u]) dfs(s,i,len);
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
cin>>(word+1);
insert(word);
}
for(int i=1;i<=m;i++)
{
cin>>(sent+1);
l=0;
memset(vis,0,sizeof(vis));
dfs(sent,1,strlen(sent+1));
printf("%d\n",l);
}
return 0;
}
Classical is something not fade,but grow more precious with time pass by,so is dream.梦想这东西和经典一样,永远不会因为时间而褪色,反而更显珍贵。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南