洛谷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
希望能帮到你 😄

浙公网安备 33010602011771号