【NOIP 2023】词典题(洛谷P9868)解题思路与代码实现详解
5个月前 (06-19)

一、题目解读
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竞赛中的中等难度题目,此代码兼顾效率与可读性,是解决词典题的经典思路。进一步优化可考虑更高效的比较算法(如二分查找),但现有方案已满足题目要求。
原创内容 转载请注明出处
