洛谷P4407 电子词典
读完这题我马上就想到了题解trie+dfs的爆搜解法,这种解法思维难度很低,算个模拟,很容易想到
但是我们稍微计算一下复杂度,就可以发现达到了
害怕T(但从实际上来看,并不会T,因为远远达不了上界),加上此题是个紫题,于是我只能想别的解法
那我们一个一个操作来思考
对于第一个操作,我们枚举每一个待查字符串的删除的字母,然后再在trie树上匹配,复杂度为
这里注意一个小细节,就是对于一个待查字符串,挨在一起的相同的字母只能删除一次(比如样例中的
对于第二个操作,我们反过来想,待查字符串添加了一个字母与已知字符串中的某一个匹配上了,是不是等价于这个已知字符串删除某一个字母然后与待查字符串匹配?于是我们就可以转换成第一个操作了,但是同样也需要注意第一个操作的那个小细节,只不过现在是对于已知字符串的注意了
本来我最初的想法是对于每一个已知字符串删除某一个字母后所能产生的不同的字符串都建立在同一颗trie树上,但这样会爆空间(一共有
于是我转换了一下,我考虑一次只删除所有已知字符串的同一位置,然后建立一个trie树,然后在进行查询(有点不好描述,麻烦看下代码,注释详细)
for(int i=1;i<=20;i++)//枚举每次删除的位置
{
init();//每次删除前记得将trie树清空
for(int j=1;j<=n;j++)
if(l[j]<i) continue;//l[j]即第j个已知字符串的长度
else if(i==1||w[j][i-1]!=w[j][i-2])//小细节
newinsert(w[j],i);//建立trie树
for(int j=1;j<=m;j++)
if(ans[j]==-1||L[j]<i) continue;//ans[j]==-1代表第j个待查字符串是已知字符串中的一个
else ans[j]+=check(q[j]);
}
这样不重不漏,复杂度也不会超
对于第三个操作,我们想一下,对于一个已知字符串和一个待查字符串,如果待查字符串可以通过第三个操作来等同于已知字符串,我们设我们是通过改变待查字符串的第
我们还需要证明一个小结论,对于通过第三个操作完成的匹配,这一组匹配的已知字符串和待查字符串一定只有
有了这个结论,我们也不需要再去注意第一个和第二个操作的细节了,因为一定不会重复的
综上所述,按照以上的解法去做这道题,我们就不会超时超空间
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e4+10;
int n,m;
int tot=1;
int trie[N*20][30],cnt[N*20],L[N],ans[N],l[N];
char w[N][30],q[N][30];
void insert(char *str)
{
int p=1,len=strlen(str);
for(int i=0;i<len;i++)
{
if(!trie[p][str[i]-'a']) trie[p][str[i]-'a']=++tot;
p=trie[p][str[i]-'a'];
}
cnt[p]++;
}
int check(char *str)
{
int p=1,len=strlen(str);
for(int i=0;i<len;i++)
if(!trie[p][str[i]-'a']) return 0;
else p=trie[p][str[i]-'a'];
return cnt[p];
}
void newinsert(char *str,int k)
{
int p=1,len=strlen(str);
for(int i=0;i<len;i++)
{
if(i==k-1) continue;
if(!trie[p][str[i]-'a']) trie[p][str[i]-'a']=++tot;
p=trie[p][str[i]-'a'];
}
cnt[p]++;
}
int solve(char *str,int k)
{
int p=1,len=strlen(str);
for(int i=0;i<len;i++)
{
if(i==k-1) continue;
if(!trie[p][str[i]-'a']) return 0;
p=trie[p][str[i]-'a'];
}
return cnt[p];
}
void init()
{
tot=1;
memset(trie,0,sizeof(trie));
memset(cnt,0,sizeof(cnt));
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",w[i]);
l[i]=strlen(w[i]);
insert(w[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%s",q[i]);
L[i]=strlen(q[i]);
if(check(q[i])) ans[i]=-1;
}
for(int i=1;i<=20;i++)
for(int j=1;j<=m;j++)
if(ans[j]==-1||L[j]<i) continue;
else if((i==1||q[j][i-1]!=q[j][i-2])) ans[j]+=solve(q[j],i);
for(int i=1;i<=20;i++)//枚举每次删除的位置
{
init();//每次删除前记得将trie树清空
for(int j=1;j<=n;j++)
if(l[j]<i) continue;//l[j]即第j个已知字符串的长度
else if(i==1||w[j][i-1]!=w[j][i-2])//小细节
newinsert(w[j],i);//建立trie树
for(int j=1;j<=m;j++)
if(ans[j]==-1||L[j]<i) continue;//ans[j]==-1代表第j个待查字符串是已知字符串中的一个
else ans[j]+=check(q[j]);
}
for(int i=1;i<=20;i++)
{
init();
for(int j=1;j<=n;j++)
if(l[j]<i) continue;
else newinsert(w[j],i);
for(int j=1;j<=m;j++)
if(ans[j]==-1||L[j]<i) continue;
else ans[j]+=solve(q[j],i);
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构