zoukankan      html  css  js  c++  java
  • Codeforces 856B

    856B - Similar Words

    题意

    如果一个字符串可以通过去掉首位字母得到另一个字符串,则称两个字符串相似。
    给出一个字符串集合,求一个新的字符串集合,满足新集合里的字符串是原字符串集合中的字符串的前缀且字符串两两不相似,问新集合里字符串的最大数量。

    分析

    注意到字符串都是基于原串的前缀,考虑用 (Trie) 优化,可以高效存储所有的前缀串。考虑相似的定义,一个较长串的相似串是可以唯一确定的(去掉首字母),通过这个关系,我们可以把两个字符串连边,最终我们可以构造出一棵树,考虑题目所求,那么我们实际上求得的是子串的集合大小减去树上的最小点覆盖集的大小,利用 树形DP 求解。

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN = 1e6 + 10;
    vector<string> v;
    vector<int> G[MAXN];
    int nxt[MAXN][26], val[MAXN], vis[MAXN], dp[MAXN][2];
    int root, L, cnt;
    int newnode() {
        memset(nxt[L], -1, sizeof nxt[L]);
        return L++;
    }
    void init() {
        L = 0;
        root = newnode();
    }
    void clear() {
        cnt = 0;
        for(int i = 0; i < L; i++) {
            val[i] = vis[i] = 0;
            G[i].clear();
        }
    }
    void insert(string S) {
        int now = root;
        for(int i = 0; i < S.length(); i++) {
            int d = S[i] - 'a';
            if(nxt[now][d] == -1) {
                nxt[now][d] = newnode();
            }
            now = nxt[now][d];
            if(!val[now]) cnt++;
            val[now] = 1;
        }
    }
    int query(string S) {
        int now2 = nxt[root][S[0] - 'a'], now = root;
        for(int i = 1; i < S.length(); i++) {
            now = nxt[now][S[i] - 'a'];
            now2 = nxt[now2][S[i] - 'a'];
            if(now == -1) break;
            if(!vis[now2] && val[now]) {
                vis[now2] = 1;
                G[now].push_back(now2);
                G[now2].push_back(now);
            }
        }
    }
    void dfs(int fa, int u) {
        vis[u] = 1;
        dp[u][0] = 0; dp[u][1] = 1;
        for(int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            if(v != fa) {
                dfs(u, v);
                dp[u][1] += min(dp[v][0], dp[v][1]); // 将当前点加入点覆盖集中
                dp[u][0] += dp[v][1]; // 当前点不加入点覆盖集
            }
        }
    }
    
    int main() {
        ios::sync_with_stdio(0);
        cin.tie(0);
        int T;
        cin >> T;
        while(T--) {
            v.clear();
            init();
            int n;
            cin >> n;
            for(int i = 0; i < n; i++) {
                string s;
                cin >> s;
                v.push_back(s);
                insert(s);
            }
            for(int i = 0; i < n; i++) {
                query(v[i]);
            }
            int rt = 1;
            for(int i = 0; i < L; i++) vis[i] = 0;
            for(int i = 0; i < L; i++) {
                if(!vis[i] && G[i].size() > 0) {
                    dfs(i, i);
                    cnt -= min(dp[i][0], dp[i][1]);
                }
            }
            cout << cnt << endl;
            clear();
        }
        return 0;
    }
    
  • 相关阅读:
    【动画】看动画轻松理解「Trie树」
    浅析HTTP/2的多路复用
    HTTPS 详解
    PHP写时复制(Copy On Write)
    golang 几种字符串的拼接方式
    正排索引和倒排索引简单介绍
    传值还是传引用
    lvs与nginx区别
    Docker运行操作系统环境(BusyBox&Alpine&Debian/Ubuntu&CentOS/Fedora)
    原创-thanos组件(聚合多个prometheus组件)原理介绍
  • 原文地址:https://www.cnblogs.com/ftae/p/7574523.html
Copyright © 2011-2022 走看看