题解:洛谷 P1481 魔族密码

【题目来源】

洛谷:P1481 魔族密码 - 洛谷

【题目描述】

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

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

  • i;
  • int;
  • integer。

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

  • integer;
  • intern。

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

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

【输入】

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

【输出】

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

【输入样例】

5
i
int
integer
intern
internet

【输出样例】

4

【解题思路】

image

【算法标签】

《洛谷 P1481 魔族密码》 #树形dp# #哈希,hash# #字典树,Trie# #差分#

【代码详解】

// 动态规划
#include <bits/stdc++.h>
using namespace std;

int n;                // 字符串数量
int dp[2005];         // dp数组,dp[i]表示以第i个字符串结尾的最长链长度
int maxlen;           // 记录最长链的长度
string s[2005];       // 存储所有字符串

int main()
{
    // 输入字符串数量
    cin >> n;
    
    // 输入所有字符串
    for (int i = 1; i <= n; i++) 
    {
        cin >> s[i];
        int maxs = 0;  // 记录当前字符串能连接的最大前驱链长度
        
        // 检查前面所有字符串是否可以作为当前字符串的前缀
        for (int j = 1; j < i; j++) 
        {
            if (s[i].find(s[j]) == 0)  // 如果s[j]是s[i]的前缀
            {
                maxs = max(maxs, dp[j]);  // 更新最大前驱链长度
            }
        }
        
        dp[i] = maxs + 1;          // 当前字符串的最长链长度
        maxlen = max(maxlen, dp[i]); // 更新全局最长链长度
    }
    
    // 输出最长链长度
    cout << maxlen;
    
    return 0;
}
// 字典树
#include <bits/stdc++.h>
using namespace std;

// 字典树节点结构体
struct node 
{
    char c;                     // 当前节点字符
    map<char, int> child;       // 子节点映射(字符->索引)
    bool end = 0;               // 标记是否为单词结尾
} root;                         // 根节点

vector<node> trie;              // 字典树存储结构
int n, maxs;                    // n: 字符串数量, maxs: 最大连续前缀数
string s;                       // 临时存储输入的字符串

/**
 * 构建字典树并计算最大连续前缀数
 * @param s 要插入的字符串
 */
void build(string s)
{
    int fa = 0;                 // 当前父节点索引(从根节点0开始)
    int count = 1;              // 当前连续前缀计数
    node x;                     // 新节点临时变量
    
    for (int i = 0; i < s.size(); i++) 
    {
        // 如果当前字符不存在于子节点中
        if (!trie[fa].child[s[i]]) 
        {
            int len = trie.size();          // 获取新节点位置
            trie[fa].child[s[i]] = len;     // 添加子节点映射
            x.c = s[i];                     // 设置节点字符
            
            // 如果是字符串最后一个字符,标记为单词结尾
            if (i == s.size() - 1) 
            {
                x.end = count;
            }
            
            trie.push_back(x);              // 添加新节点
            fa = len;                       // 移动到新节点
        } 
        else 
        {
            // 字符已存在,移动到对应子节点
            fa = trie[fa].child[s[i]];
            
            // 如果遇到单词结尾,增加计数并更新最大值
            if (trie[fa].end) 
            {
                count++;
                maxs = max(maxs, count);
            }
        }
    }
}

int main()
{
    cin >> n;                    // 输入字符串数量
    trie.push_back(root);        // 初始化字典树根节点
    
    // 处理每个字符串
    for (int i = 0; i < n; i++) 
    {
        cin >> s;
        build(s);                // 构建字典树并计算
    }
    
    cout << maxs;               // 输出最大连续前缀数
    return 0;
}

【运行结果】

5
i
int
integer
intern
internet
4
posted @ 2026-02-19 14:21  团爸讲算法  阅读(1)  评论(0)    收藏  举报