NOIP2023

发布时间 2023-11-20 21:08:09作者: V_Melville

T1:词典

题意:

给定 \(n\) 个长度为 \(m\) 的字符串 \(w_1, w_2, \cdots, w_n\)
对于每个 \(i = 1, 2, \cdots, n\) 询问是否存在 \(w_1', w_2', \cdots, w_n'\) 使得对于每个 \(j = 1, 2, \cdots, n\)\(w_j'\) 都可以由 \(w_j\) 交换字符得到,且对于 \(j \neq i\) 都有 \(w_i'\) 的字典序小于 \(w_j'\)

数据范围:

\( 1 \leqslant n \leqslant 3000 \), \( 1 \leqslant m \leqslant 3000 \), 所有字符串均由小写字母构成

算法分析

做法1:

对于每个 \(i\), 如果存在方案的话必然可以让 \(w_i'\) 字典序最小,而其他的 \(j \neq i\)\(w_j'\) 字典序最大。也就是让 \(w_i\) 的字符从小到大排序得到 \(w_i'\),让 \(w_j\) 的字符从大到小排序得到 \(w_j'\)

直接模拟这个过程的时间复杂度为 \(O(n^2m)\),期望得分 \(80\) 分。
但实际上可以拿 \(100\) 分。

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    freopen("dict.in", "r", stdin);
    freopen("dict.out", "w", stdout);
    
    int n, m;
    cin >> n >> m;
    
    vector<string> w(n);
    rep(i, n) cin >> w[i];
    
    vector<string> mn(n);
    rep(i, n) {
        mn[i] = w[i];
        sort(mn[i].begin(), mn[i].end());
    }
    vector<string> mx(n);
    rep(i, n) {
        mx[i] = w[i];
        sort(mx[i].rbegin(), mx[i].rend());
    }
    
    string ans;
    rep(i, n) {
        bool ok = true;
        rep(j, n) if (j != i) {
            if (mn[i] > mx[j]) {
                ok = false;
                break;
            }
        }
        ans += ok ? '1' : '0';
    }
    
    cout << ans << '\n';
    
    return 0;
}

做法2:

实际上我们发现没有必要模拟上述过程,因为得到的 \(w_i'\) 以及 \(w_j'\) 一定是一段一段的,最多 \(\sigma = 26\) 段,使用桶存下每个字符的出现次数,之后一段一段比较即可在 \(O(nm+\sigma n^2)\) 的复杂度内解决。期望得到 \(80 \sim 100\) 分。

做法3:

不妨再深入一步,设 \(f_i\) 表示 \(w_i\) 中出现的最小字母, \(g_i\) 表示 \(w_i\) 中出现的最大字母。
如果 \(f_i < g_i\),那么显然 \(w_i'\) 的字典序小于 \(w_j'\),只需要将 \(f_i\) 作为 \(w_i'\) 的第一个字母,\(g_j\) 作为 \(w_j'\) 的第一个字母即可。
如果 \(f_i > g_j\),那么显然 \(w_i'\) 的字典序大于 \(w_j'\)
剩下最后一种情况 \(f_i = g_j\),想要 \(w_i'\) 的字典序最小,需要将 \(f_i\) 放在最前面,此时越往后 \(w_i'\) 的字母越大。想要 \(w_j'\) 的字典序最大,需要将 \(g_j\) 放在最前面,此时越往后 \(w_j'\) 的字母越小。因此此时必然有 \(w_i'\) 的字典序大于 \(w_j'\)
综上,如果 \(i\) 可能,当且仅当 \(f_i < \min\limits_{j \neq i} g_j\)。因此可以 \(O(n)\) 完成这个过程,总的时间复杂度为 \(O(nm+n^2)\)
期望得分:100分。

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    freopen("dict.in", "r", stdin);
    freopen("dict.out", "w", stdout);
    
    int n, m;
    cin >> n >> m;
    
    vector<int> f(n, 26), g(n, -1);
    rep(i, n) {
        string w;
        cin >> w;
        rep(j, m) {
            f[i] = min(f[i], w[j]-'a');
            g[i] = max(g[i], w[j]-'a');
        }
    }
    
    string ans;
    rep(i, n) {
        bool ok = true;
        rep(j, n) if (j != i) {
            if (f[i] >= g[j]) {
                ok = false;
                break;
            }
        }
        ans += ok ? '1' : '0';
    }
    
    cout << ans << '\n';
    
    return 0;
}