洛谷P1481 魔族密码

P1481 魔族密码

题目背景

风之子刚走进他的考场,就……

花花:当当当当~~偶是魅力女皇——花花!!^^(华丽出场,礼炮,鲜花)

风之子:我呕……(杀死人的眼神)快说题目!否则……-_-###

题目描述

花花:……咦好冷我们现在要解决的是魔族的密码问题(自我陶醉:搞不好魔族里面还会有人用密码给我和菜虫写情书咧,哦活活,当然是给我的比较多拉*_*)。

魔族现在使用一种新型的密码系统。每一个密码都是一个给定的仅包含小写字母的英文单词表,每个单词至少包含 \(1\) 个字母,至多 \(75\) 个字母。如果在一个由一个词或多个词组成的表中,除了最后一个以外,每个单词都被其后的一个单词所包含,即前一个单词是后一个单词的前缀,则称词表为一个词链。例如下面单词组成了一个词链:

  • \(\verb!i!\)
  • \(\verb!int!\)
  • \(\verb!integer!\)

但下面的单词不组成词链:

  • \(\verb!integer!\)
  • \(\verb!intern!\)

现在你要做的就是在一个给定的单词表中取出一些词,组成最长的词链,就是包含单词数最多的词链。将它的单词数统计出来,就得到密码了。

风之子:密码就是最长词链所包括的单词数阿……

输入格式

这些文件的格式是,第一行为单词表中的单词数 \(N\)\(1 \le N \le 2000\)),下面每一行有一个单词,按字典顺序排列,中间也没有重复的单词。

输出格式

输出共一行,一个整数,表示密码。

输入输出样例 #1

输入 #1

5
i
int
integer
intern
internet

输出 #1

4

P1481 魔族密码


该题适用于字典树做法

什么是字典树?(已经学习过的博友请直接移步至题目解析)


字典树类似于树状结构
例如 我们想存储

abc
acd

两个字符串
别问为什么不用数组,因为实际中字符串可能更多,数组容易爆
我们为了优化算法复杂度,构建了这样一个树:
字典树

什么意思?


字典树的每条对应一个字符,每个对应一个节点
当沿着该图的左线遍历,我们得到了字符串:

abc

当沿着该图的右线遍历,我们得到了字符串:

acd

于是以及其优秀的复杂度存储下了这两个字符串,这种方法推广到几十种字符串或者上万种字符串,就是字典树

题目解析


首先既然是树状数组,就需要定义一个trie(也可以是tree)

int trie[N][26];
/*
trie[i][j]表示节点i的第j个子节点的编号
单词长度由N决定
子节点只有a~z一共26种可能
*/

然后我们需要用一个f数组记录该节点是否是结尾节点:

int f[N];

以及一个初始节点root(默认为0,因为节点都从0开始)

int root=0;

还需要一个记录总数的tot变量

int tot=0;

接下来是函数部分

插入函数(ins)


我们用

void ins(string s);

表示将s插入到字典树里
具体写法及解析:

void ins(string s)
{
	int p=root; //p表示节点位置,初始时为root
	for(auto c:s) //一种简便写法,适用于新版本c++语言
	{
		//这里如果trie[p][c-'a']=0,说明此时p节点的c字符子节点还没记录,应该记录下来,同时总数+1
		//如果记录过了,直接跳到下一个节点继续遍历即可
		if(trie[p][c-'a']==0) trie[p][c-'a']=++tot;
		//记录完毕后,就跳到该子节点,继续遍历下一个
		p=trie[p][c-'a'];
	}
	f[p]=1; //不要忘了,遍历完后p的位置时单词s的结尾节点,应坐上标记
}

答案函数


我们用

int query(string s)

来获得该题的答案,即最长链的长度
具体写法及解析:

int query(string s)
{
	int ans=0; //记录答案
	int p=root; //老规矩,从起始点开始
	for(auto c:s)
	{
		p=trie[p][c-'a']; //对于每一个节点的编号赋值给对应的节点
		ans+=f[p]; //如果该节点是结尾结点说明我们找到了继上一个单词的下一个单词,链的长度+1
		//如果不是结尾结点,那么f[p]=0,接着往下找
	}
	return ans; //最后返回ans
}

解决完两个函数问题,我们就可以顺理成章地写出代码了

ACcode:

#include<bits/stdc++.h>
using namespace std;

const int N=150005;
string s[N];
int f[N],root,tot,n;
int trie[N][26];
void ins(string s)
{
	int p=root;
	for(auto c:s)
	{
		if(trie[p][c-'a']==0) trie[p][c-'a']=++tot;
		p=trie[p][c-'a'];
	}
	f[p]=1;
}
int query(string s)
{
	int ans=0;
	int p=root;
	for(auto c:s)
	{
		p=trie[p][c-'a'];
		ans+=f[p];
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(0); //这条语句是加速cin和cout用的,缺点是不能再使用scanf和printf
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>s[i]; //读入字符串
		ins(s[i]); //插入s[i]
	}
	int ans=0;
	for(int i=1;i<=n;i++) ans=max(ans,query(s[i])); //取出最大的链
	cout<<ans<<endl; //最后输出
	return 0;
}
//Author:AAA_jiancaipifa

希望能帮到你 😄

posted @ 2026-05-14 20:47  AAA_jiancaipifa  阅读(15)  评论(0)    收藏  举报