Luogu P2292 [HNOI2004]L语言
解析
-
看到单词和句子匹配,再看数据范围1M的字符串(长度大约 $ 10^6 $ 级别),所以用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
",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
",l);
}
return 0;
}