首页 > 其他分享 >洛谷P4407 电子词典

洛谷P4407 电子词典

时间:2023-07-28 22:22:12浏览次数:45  
标签:洛谷 已知 待查 int 1e4 字符串 20 P4407 电子词典

读完这题我马上就想到了题解trie+dfs的爆搜解法,这种解法思维难度很低,算个模拟,很容易想到
但是我们稍微计算一下复杂度,就可以发现达到了\(1e8\)级别(\(26*20*20*1e4\),即对于每一个待查字符串(\(1e4\)),枚举每一个位置(\(20\)),每一个位置枚举26个字母(\(26\)),然后再在trie树上匹配(\(20\)))
害怕T(但从实际上来看,并不会T,因为远远达不了上界),加上此题是个紫题,于是我只能想别的解法
那我们一个一个操作来思考
对于第一个操作,我们枚举每一个待查字符串的删除的字母,然后再在trie树上匹配,复杂度为\(1e4*20*20\)
这里注意一个小细节,就是对于一个待查字符串,挨在一起的相同的字母只能删除一次(比如样例中的\(abcdd\)的最后两个\(dd\),在枚举时只用枚举一次就可以了,不然会重复计数;可以证明,只有这种情况才会导致重复计数)
对于第二个操作,我们反过来想,待查字符串添加了一个字母与已知字符串中的某一个匹配上了,是不是等价于这个已知字符串删除某一个字母然后与待查字符串匹配?于是我们就可以转换成第一个操作了,但是同样也需要注意第一个操作的那个小细节,只不过现在是对于已知字符串的注意了
本来我最初的想法是对于每一个已知字符串删除某一个字母后所能产生的不同的字符串都建立在同一颗trie树上,但这样会爆空间(一共有\(1e4\)个原始已知字符串,每一个已知字符串最多有\(20\)个字母,故能产生\(20\)个新字符串,所有新字符串一共有\(20*1e4\)个,即trie树上有\(20*1e4*20\)个节点,每个节点开\(26\)个子节点,故一共有\(20*1e4*26*20\)个int类型,大约400MB)
于是我转换了一下,我考虑一次只删除所有已知字符串的同一位置,然后建立一个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]);
	}

这样不重不漏,复杂度也不会超
对于第三个操作,我们想一下,对于一个已知字符串和一个待查字符串,如果待查字符串可以通过第三个操作来等同于已知字符串,我们设我们是通过改变待查字符串的第\(i\)个字母来达到已知字符串的,那么同时删除已知字符串和待查字符串的第\(i\)个字母是不是剩余的字符串就要相等了?所以这就是我们第三个操作的等价转换
我们还需要证明一个小结论,对于通过第三个操作完成的匹配,这一组匹配的已知字符串和待查字符串一定只有\(i\)这一个位置是不同的,这里建立在这个待查字符串不是已知字符串中的一个的前提下。这个结论比较简单,反证法即可
有了这个结论,我们也不需要再去注意第一个和第二个操作的细节了,因为一定不会重复的
综上所述,按照以上的解法去做这道题,我们就不会超时超空间

#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;
}

标签:洛谷,已知,待查,int,1e4,字符串,20,P4407,电子词典
From: https://www.cnblogs.com/dingxingdi/p/17589021.html

相关文章

  • 洛谷 P1347 排序 - 拓扑排序
    P1347排序题意依次给一些具有排序关系的序列,问你在能否在若干个序列之后确定元素的顺序、判断元素关系存在矛盾、判断无法确认元素顺序思路对于每一个排序关系均进行toposort,后面就是toposort判环(出现矛盾),toposort判顺序,无法确认唯一关系。详见代码或看洛谷题解区代码......
  • [百紫祭] 洛谷P5840做题笔记
    [百紫祭]洛谷P5840做题笔记luogu传送门前置芝士:AC自动机,树上差分,树剖求LCA,树状数组。前言一篇笔记需要一张头图。题意给出\(n\)个字符串\(S_1,S_2\.\.\.\S_n\)和一个初始为空的字符串集合\(T\),接下来\(q\)次操作。操作分为修改和询问。修改操作为向\(T\)中......
  • 洛谷 P2894 [USACO08FEB] Hotel G 题解
    题目链接P2894[USACO08FEB]HotelG-洛谷|计算机科学教育新生态(luogu.com.cn)分析考虑用线段树维护区间信息维护sum(最大连续空房间数)如何合并?sum1为max(sum2,sum3)(1的两个子区间)但我们发现若区间为100001(0表示空房间)sum1=4而max(sum2,sum3)=2所以再维护suml(从左开始的......
  • 洛谷 P9221 「TAOI-1」Pentiment 题解
    Description给定\(n\timesm\)的矩阵,从第\(1\)行任意格子出发,每次向下、左、有走一步,有\(q\)个障碍不能经过,求走到第\(n\)行任意格子的方案。对于所有数据,\(1\leqn,m\leq10^9\),\(1\leqq\leq10^5\)。link:https://www.luogu.com.cn/problem/P9221Solution算法一考......
  • 洛谷 P3291 [SCOI2016] 妖怪
    设每只怪物经过环境影响后的攻击力和防守力分别为\(x_i,y_i\),则有:\(y_i=dnf_i-\dfracba(x_i-atk_i)\)。设\(k=-\dfracba\),则有\(y_i=kx_i+dnf_i-k\cdotatk_i\)。设直线\(l_i:y_i=kx_i+dnf_i-k\cdotatk_i\),第\(i\)只怪物在\((a,b)\)的环境下......
  • 洛谷P3629 [APIO2010] 巡逻题解
    题目链接P3629[APIO2010]巡逻-洛谷|计算机科学教育新生态(luogu.com.cn)思路n个村庄,n-1条道路,原图为树1.若k=0(不修建道路)那么答案为(n-1)*2 每个道路会走两遍2.若k为1(修建一条道路)设修建的道路(r1)所在的环长度为L那么答案为(n-1)*2-L+2可以看到r1所在环的道路只走了......
  • 洛谷CF1738C题解
    好一道博弈论水题题目传送门更好的食用体验题目大意:给定长度为$n$的数列$a_1,a_2,\dots,a_n$,两名玩家Alice、Bob依次以最优策略从数列中取走一个数,Alice先取,直至为空博弈结束。若Alice取走的所有数之和为偶,Alice胜利;若Alice取走的所有数之和为奇,Bob胜利......
  • 洛谷 T356695 文字处理软件(重置版)
    很简单了啊!说普及-我都不信作者(也就是我)链接:https://www.luogu.com.cn/problem/T356695好好想想!!!!题目!文字处理软件(重置版)题目背景Allow是一名程序员,他要为公司开发一款“文字处理软件”!题目描述用户可能输入∞个数字。说白了用while(1)输入1时,把字符串原样输出。......
  • 洛谷AT_jsc2019_qual_e Card Collector 题解
    题目链接CardCollector-洛谷|计算机科学教育新生态(luogu.com.cn)思路将每一行、每一列转化为点,第i行第j列的卡牌转化为i->j+m(m为行数)的有向边。总共会抽取m+n(m为行数,n为列数)张牌,每个点的出度为1。结果图为基环森林;那么题目就转化为求最大基环森林。代码1#include......
  • 洛谷3294 背单词
    这题乍一看是排序贪心,然后使用领项交换来做题由于有了第一条规则的存在,因为\(n*n\)远大于另外两条规则所产生的代价,所以我们不会让后缀排在后面于是乎,我们倒序建立trie树并且重构树(具体可见洛谷题解),那么问题就转换为给这棵树标号,要求必须标了父亲才能标儿子,令每一条边的代价为......