1 条题解

  • 0
    @ 2025-8-24 21:59:51

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Utsuji_risshū
    **

    搬运于2025-08-24 21:59:51,当前版本为作者最后更新于2018-07-07 18:15:02,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这道题用Trie树做的话,建好树之后对于每一个查询单词,其实就是暴力枚举出其添加1个字母、替换1个字母或者删去1个字母后的字符串在不在树上。具体枚举的话可以循环模拟但我太弱了写不出来,有点繁。这里我用的是DFS,相当于是在Trie树上做3种查找,等效于3种编辑方式。我们用DFS(root,length,f)表示目前在查root节点的孩子中是否有s[length]这一位的字符,f表示“编辑”机会是否用过。

    1.删除:相当于忽略当前字符在树上的存在,直接跳到下一个字符继续找,即进行DFS(root,length+1,1);

    2.添加:相当于在root孩子中继续查剩下s[length]~s[size-1]的串,即进行DFS(Trie[root][i],length,1),这里是要枚举26种情况的;

    3.替换:相当于在root孩子中继续查剩下s[length+1]~s[size-1]的串,即进行DFS(Trie[root][i],length+1,1),这里也要枚举26种情况,并且替换字母不能和后一位字符相同

    此外,除以上3种查询方式,还有最基本的Trie树查询,即字符串不经过修改是否存在。然后就没什么问题了,注意防止不同的修改出现相同串即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=10000+7;
    int Trie[MAXN*20][26],visx[MAXN];//visx[i]存足修改要求后的字符串末节点 
    char s[22];
    bool p[MAXN*20],vis[MAXN*20],word;//p表示p[rt]是否是单词节点,vis[rt]表示rt是否已经是满足修改要求后的字符串 
    int n,m,u,len,tot,vistot;
    
    void Insert(){
    	u=0,len=strlen(s);
    	for(register int i=0;i<len;++i){
    		int c=s[i]-'a';
    		if(!Trie[u][c]) Trie[u][c]=++tot;
    		u=Trie[u][c];
    	}
    	p[u]=1;
    }
    
    void DFS(int rt,int l,bool f){
    	if(l==len&&p[rt]&&!f){
    		word=1;return;
    	}//Trie树本来就存在的 
    	if(l==len&&p[rt]&&f){
    		if(!vis[rt]) vis[visx[++vistot]=rt]=1;
    		return;
    	}//经过修改而存在的标记下来,那句赋值没看懂的话就是vis标记一下,同时往visx里装 
    	int c=s[l]-'a';
    	if(!f){
    		if(l<len) DFS(rt,l+1,1);//l=len无意义 
    		for(register int i=0;i<26;++i)
    			if(Trie[rt][i]){
    				DFS(Trie[rt][i],l,1);
    				if(i!=c) DFS(Trie[rt][i],l+1,1);
    			}//添加和替换可以合起来 
    	}
    	if(l>=len) return;//长度到了但没单词,返回。注意到len长度是也是可以添加的。 
    	if(Trie[rt][c]) DFS(Trie[rt][c],l+1,f);//直接查 
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(register int i=1;i<=n;++i) scanf("%s",s),Insert();
    	for(register int i=1;i<=m;++i){
    		scanf("%s",s);len=strlen(s);
    		DFS(0,0,0);
    		if(word) printf("-1\n");
    		else printf("%d\n",vistot);
    		while(vistot) vis[visx[vistot--]]=0;//把记录都倒掉 
    		word=0;
    	}
    }
    

    感谢JimmyL发现的一处错误

    • 1

    信息

    ID
    3361
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者