Processing math: 100%

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;
}
posted @   Hawking_llfz  阅读(126)  评论(0编辑  收藏  举报
编辑推荐:
· 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代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示