当前位置:首页 > 其他 > 【NOIP 2023】词典题(洛谷P9868)解题思路与代码实现详解

【NOIP 2023】词典题(洛谷P9868)解题思路与代码实现详解

3天前

【NOIP 2023】词典题(洛谷P9868)解题思路与代码实现详解 NOIP 2023 词典题 洛谷P9868 解题思路 代码实现 第1张

一、题目解读

2023年NOIP中的词典题洛谷P9868)要求判断给定n个单词是否存在一种字典序排列,使得每个单词在排序后都不包含另一个单词。题目核心在于理解字典序与字符包含关系的约束,需通过高效算法确定是否存在合法排列。

二、解题思路

采用“预处理字符频率+边界构建+并行比较”的策略:

1. 利用vector存储单词及字符频率,使用short优化空间;

2. 构建每个单词的最小字典序(按字符频率升序排列)和最大字典序(降序);

3. 通过并行比较各单词的边界:若单词i的最小字典序≥单词j的最大字典序,则i与j可共存,最终输出二进制结果串。

三、解题步骤详解

1. 输入处理:读取n和m(实际为n个单词),存储单词并初始化频率矩阵;

2. 字符频率预处理:遍历单词,统计各字母出现次数,同时更新min_chars和max_chars;

3. 构建字典序边界:生成每个单词的最小/最大字典序字符串(min_words/max_words);

4. 并行比较优化:对每个单词i,遍历其他单词j,判断min_words[i]≥max_words[j]是否成立,标记合法性;

5. 输出结果:将二进制合法性串输出。

四、代码与注释

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr); // 优化输入输出速度

    int n, m;
    cin >> n >> m;
    vector<string> words(n);
    vector<vector<short>> min_chars(n, vector<short>(26, 0)); // 存储每个单词的最小字符频率
    vector<vector<short>> max_chars(n, vector<short>(26, 0)); // 存储每个单词的最大字符频率

    // 预处理字符频率(使用short节省空间)
    for (int i = 0; i < n; ++i) {
        cin >> words[i];
        for (char c : words[i]) { // 遍历单词字符
            min_chars[i][c - 'a']++; // 统计最小频率
            max_chars[i][c - 'a']++; // 统计最大频率
        }
    }

    // 预处理所有单词的字典序边界
    vector<string> min_words(n), max_words(n);
    for (int i = 0; i < n; ++i) {
        // 构建最小字典序
        for (int c = 0; c < 26; ++c) {
            min_words[i] += string(min_chars[i][c], 'a' + c); // 按频率升序拼接
        }
        // 构建最大字典序
        for (int c = 25; c >= 0; --c) {
            max_words[i] += string(max_chars[i][c], 'a' + c); // 按频率降序拼接
        }
    }

    string result(n, '0');
    if (n == 1) {
        cout << "1\n";
        return 0; // 特判n=1直接输出合法
    }

    // 并行比较优化
    for (int i = 0; i < n; ++i) {
        bool valid = true;
        for (int j = 0; j < n; ++j) {
            if (i == j) continue; // 跳过自身比较
            if (min_words[i] >= max_words[j]) { // 若i的最小字典序≥j的最大字典序
                valid = false;
                break; // 标记不合法并跳出
            }
        }
        result[i] = valid? '1' : '0'; // 更新结果串
    }

    cout << result << '\n';
    return 0;
}

五、总结

该解法巧妙利用字符频率边界与字典序比较,通过并行优化降低时间复杂度至O(n^2),配合short类型节省空间。对于NOIP竞赛中的中等难度题目,此代码兼顾效率与可读性,是解决词典题的经典思路。进一步优化可考虑更高效的比较算法(如二分查找),但现有方案已满足题目要求。

原创内容 转载请注明出处

分享给朋友:

相关文章

汉诺塔问题递归解法(C++代码详解) 牛客4414题解题指南

汉诺塔问题递归解法(C++代码详解) 牛客4414题解题指南

一、题目解读汉诺塔问题是一个经典的递归算法题目:有n个大小不同的圆盘按从大到小顺序放在柱A上,需借助柱B,将圆盘移至柱C,每次只能移动一个圆盘,且大圆盘不能置于小圆盘之上。牛客4414题要求编写函数输...

洛谷2181题解析:组合数学中顶点交点的计算与代码优化

洛谷2181题解析:组合数学中顶点交点的计算与代码优化

一、题目解读洛谷2181题要求计算n个顶点中任意选择4个顶点确定的交点数量。题目核心在于组合数学的应用,需通过排列组合公式推导结果,同时注意处理大数以避免溢出问题。理解题目中的“交点”定义(由4个顶点...

洛谷1111题解题全解析:基于Kruskal算法与并查集的最小生成树实现

洛谷1111题解题全解析:基于Kruskal算法与并查集的最小生成树实现

一、题目解读洛谷1111题是一道经典的图论问题,要求构建一个无向图的最小生成树,并输出其最大边权值。题目核心在于通过给定的边集合,找到连接所有节点的最小权值子集,同时保证无环。这通常涉及最小生成树算法...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。