Nomomo Round 5 D. String

传送门

题意:
给定四种操作,包含插入、删除、询问字符串以及替换前缀。对于所有的询问,回答以\(s\)为前缀的字符串的值的和。
操作数\(n\leq 3\cdot 10^5\),字符串总长\(10^6\)

思路:
直接通过字典树模拟即可。
对于插入、删除、询问操作是很好解决的,主要在于第四个替换前缀的操作。
我们给每个结点维护两个值,一个是以当前结点作为结尾的字符串的权值和,一个是以当前结点结束的串作为前缀的值的和。那么我们要替换前缀,先找到\(s\)串最后一个结点所在位置\(x\)。之后给根到\(x\)结点父亲都减去sum[x],并且值加入到根到\(t\)串最后一个字符这条链中。因为我们还要转移所有的儿子,所以可以直接暴力dfs进行合并。因为这是01trie,所以合并就很方便。
复杂度分析:显然复杂度主要应该考虑dfs这部分。显然合并时只有两个结点都存在儿子才会被访问,那么合并的复杂度就是min(子树大小)的。考虑每个点的贡献,一个结点要被合并时,不妨认为他所在子树较小。那么一个结点最多会被合并logn次,所以复杂度为\(O(nlogn)\)的。
代码还有点细节,主要在于串不存在情况的判断,如果sum[x] or ed[x]为0说明不存在相应的串,这里我一开始没注意。另外就是合并的细节了,这个题我们还需要维护每个结点的father才行。
细节见代码:

// Author : heyuhhh
// Created Time : 2020/08/15 20:34:00
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e7 + 5;

int _;

int ch[N][2], fa[N], tot;
ll sum[N], ed[N];

int newnode() {
    ++tot;
    ch[tot][0] = ch[tot][1] = sum[tot] = ed[tot] = fa[tot] = 0;
    return tot;
}

int ins(const string& s, int v) {
    int n = s.length();
    int rt = 0;
    for (int i = 0; i < n; i++) {
        if (ch[rt][s[i] - '0'] == 0) {
            ch[rt][s[i] - '0'] = newnode();
        }
        fa[ch[rt][s[i] - '0']] = rt;
        rt = ch[rt][s[i] - '0'];
        sum[rt] += v;
    }
    ed[rt] += v;
    return rt;
}

void del(const string& s) {
    int n = s.length();
    int rt = 0;
    for (int i = 0; i < n; i++) {
        rt = ch[rt][s[i] - '0'];
        if (rt == 0) {
            cout << "Not Exist" << '\n';
            return;
        }
    }
    ll v = ed[rt];
    // 是否存在这个子串
    if (v == 0) {
        cout << "Not Exist" << '\n';
        return;
    }
    ed[rt] = 0;
    while (rt) sum[rt] -= v, rt = fa[rt];
}

ll find(const string& s) {
    int n = s.length();
    int rt = 0;
    for (int i = 0; i < n; i++) {
        rt = ch[rt][s[i] - '0'];
        if (rt == 0) return 0;
    }
    return sum[rt];
}

void dfs(int x, int y) {
    sum[y] += sum[x];
    ed[y] += ed[x];

    if (ch[x][0] && ch[y][0]) {
        dfs(ch[x][0], ch[y][0]);
    } else if (ch[x][0]) {
        fa[ch[y][0] = ch[x][0]] = y;
    }

    if (ch[x][1] && ch[y][1]) {
        dfs(ch[x][1], ch[y][1]);
    } else if (ch[x][1]) {
        fa[ch[y][1] = ch[x][1]] = y;
    }
}

void merge(const string& s, const string& t) {
    int n = s.length(), m = t.length();
    int rt = 0;
    for (int i = 0; i < n; i++) {
        rt = ch[rt][s[i] - '0'];
        if (rt == 0) {
            cout << "Not Exist" << '\n';
            return;
        }
    }
    // 注意判断
    if (sum[rt] == 0) {
        cout << "Not Exist" << '\n';
        return;
    }
    int x = rt;
    ll v = sum[rt];
    // 删除
    rt = fa[rt];
    while (rt) sum[rt] -= v, rt = fa[rt];
    ch[fa[x]][s.back() - '0'] = 0;
    
    // 插入
    rt = 0;
    for (int i = 0; i < m; i++) {
        if (ch[rt][t[i] - '0'] == 0) {
            ch[rt][t[i] - '0'] = newnode();
        }
        fa[ch[rt][t[i] - '0']] = rt;
        rt = ch[rt][t[i] - '0'];
        sum[rt] += v;
    }

    // 最后一个点要去掉
    sum[rt] -= v;
    int y = rt;
    // 合并
    dfs(x, y);
}

void run() {
    ++_;
    cout << "Case " << _ << endl;
    tot = -1; newnode();
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        string op, s;
        cin >> op >> s;
        if (op == "I") {
            int v; cin >> v;
            ins(s, v);
        } else if (op == "D") {
            del(s);
        } else if (op == "Q") {
            ll res = find(s);
            cout << res << '\n';
        } else {
            string t;
            cin >> t;
            merge(s, t);
        }
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}
posted @ 2020-08-16 22:46  heyuhhh  阅读(123)  评论(0编辑  收藏  举报