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;
}
重要的是自信,一旦有了自信,人就会赢得一切。