• hihoCoder #1902 字符替换


    解法

    这题比赛时过的人很多,我却没思路,糊里糊涂写了个强联通分量,得了 80 分。

    这题思路是这样的。

    一个替换操作可以看做一个有向边,所以题目实际上给出了一个有向图 $G$,一个节点代表一个字母。

    注意题目要求每个操作都必须执行一次。

    关于自环

    首先注意到自环是没有意义的,因此处理输入时把自环忽略掉。

    这里需要特别说明自环的问题,题目描述中并没有说明 $X_i e Y_i$。不过似乎可以合理地假设输入中不存在 $X_i = Y_i$ 的操作。

    有些 AC 的代码并没有判断自环,比如冰心水蜜桃的提交。当输入中有自环时,这个代码是有 BUG 的。

    关于重边

    实际上重边也是没有意义的,但是我们不必特别处理它。


    用 $mathsf{h}$ 表示 $G$ 中对应于字符 ‘h’ 的节点。

    设字符 $x$ 在串 $S$ 中出现过且 $x$ 不是 ‘h’ 将 $x$ 出现的次数记做 $c_x$ 。则这 $c_x$ 个 $x$ 能转变为 ‘h’ 的充要条件是「图 $G$ 中存在一条从 $x$ 到 ‘h’ 的简单路径」。

    证明:不失一般性,设 $x o x_1 o x_2 o mathsf{h}$ 是一条从 $x$ 到 ‘h’ 的简单路径,则我们可以按下述方法将 $x$ 变为 ‘h’。

    首先将 $x$ 变为 $x_1$,这个操作用掉了 $(x o x_1)$ 这条边;再将剩下的从 $x$ 发出的边全部用掉,这些边将不换改变当前的字符串,然后把所有从 $x$ 发出的边从图 $G$ 中删除。以此类推。

    不难注意到若 ‘h’ 有出边,则上述论证是有问题的。

    ‘h’ 的出度为零的情形是平凡的。考虑 ‘h’ 的出度不为零的情形。此时若图 $G$ 中不存在「从 ‘h’ 到 ‘h’ 的回路」,则若初始字符串 $S$ 中有 ‘h’,则这些 ‘h’ 终将变成别的字符。因此在这种情况下我们可以将 ‘h’ 的所有出边先执行一遍,并把这些边从图 $G$ 中删除。

    这样就完成了上述论证。

    不过至此我们只是针对一个字符 $x$ 进行论证。实际上对多个字符,结论是一样的,证明留给读者。


    现在来考虑图 $G$ 中存在从 ‘h’ 到 ‘h’ 的回路的情形。注意这样的回路一定不是自环。任取一个从 ‘h’ 到 ‘h’ 的回路 $C$,我们可以先把 'h' 变成回路 $C$ 上 'h' 的后继,得到一个新字符串 $S'$,并把图 $G$ 中其他的 ‘h’ 的出边删除。这样就把问题规约为上一段所描述的情形。

    实现

    我们需要判断的是,对于字符 $xin S$ 且 $x emathsf{h}$,图 $G$ 中是否有一条从 $x$ 到 $mathsf{h}$ 的简单路径,这可以通过 DFS 完成。另外当 $mathsf{h}$ 的出度不为零时我们需要判断图 $G$ 中是否存在一条从 $mathsf{h}$ 到 $mathsf{h}$ 的回路。先进行DFS,确保字符串 $S$ 中所有字符都被访问过。遍历每一条以 $mathsf{h}$ 为起点的边 $(mathsf{h} o x)$,判断图 $G$ 中是否存在从 $x$ 到 $mathsf{h}$ 的简单路径。

    也可以把边反向以后建图,这样只要对 $mathsf{h}$ 调用一次 DFS 就可以了。

    Implementation

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        //freopen("main.in", "r", stdin);
        int n; cin >> n;
        string s; cin >> s;
        vector<int> cnt(26);
        for(auto ch: s) cnt[ch-'a']++;
        vector<vector<int>> g(26);
        vector<bool> to_h(26);
        bool flag = false;
        while (n--) {
            char x, y; cin >> x >> y;
            if (x != y) {
                g[y-'a'].push_back(x-'a');
                if(x == 'h') {
                    to_h[y-'a'] = true;
                    flag = true;
                }
            }
        }
        
        function<void(int)> dfs;
        vector<bool> vis(26);
        dfs = [&](int u) {
            vis[u] = true;
            for(auto v: g[u]) {
                if(!vis[v]) dfs(v);
            }
        };
        int h = 'h' - 'a';
        dfs(h);
        
        int ans = 0;
        for (int i = 0; i < 26; i++)
            if(vis[i]) ans += cnt[i];
        if (flag) {
            for (int i = 0; i < 26; i++)
                if (to_h[i] && vis[i]) {
                    flag = false;
                    break;
                }
            if (flag) ans -= cnt[h];
        }
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    mysql 视图
    CSS 上下居中和最低高度语法
    escape()、encodeURI()、encodeURIComponent()区别详解
    YII事件EVENT示例
    linux history命令优化
    mysql 之full join
    redis学习之数据类型
    <canvas>设置宽高遇到的问题
    关于块级元素撑满整个浏览器窗口
    jquery中bind()绑定多个事件
  • 原文地址:https://www.cnblogs.com/Patt/p/10170820.html
Copyright © 2020-2023  润新知