CF1437G Death DBMS
洛谷link
2025/12/13 考试t4
一眼ac自动机,暴力跳fail;
然后发现有名字一样的,同一个节点上有可能叠加很多权值;考试的时候想了个记录的方法过了,结果是考试的数据过水,我的做法实际上是错解;
这个时候就要用multiset来维护一个节点上的权值;
multiset
multiset<int> st[N]
//建立一个二位multiset
//可以将其中的int类型改为自定义的类型,在结构体里写operator定义排序方法(类似优先队列)
st[i].insert(val);
//插入值val
st[i].erase(val);
//把所有值为val的都删掉
auto f = st[i].find(val);//找到值为val的, 没有则返回st[i].end();
if(f != st[i].end()) st[i].erase(f);
//删掉一个值为val的
st[i].begin();
//最小值(会自动从小到大排序)
auto f = st[i].end();
if(!st[i].empty()) f--;
//最大值
st[i].count(val);
//值val出现次数
st[i].clear();
//清空
于是乎在本题中对与每一个节点建一个multiset,对于每一个修改先把上一个这个人的值删除, 再加入当前值, 跳fail时直接在集合中找最大值即可
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int n, m, w[N], tr[N][30], g[N], fail[N], vis[N], cnt;
multiset<int> st[N];
char s[N], t[N];
void build(char *s, int b){ //建字典树
int p = 0;
for(int i = 0, len = strlen(s); i < len; i++){
if(!tr[p][s[i] - 'a']) tr[p][s[i] - 'a'] = ++cnt;
p = tr[p][s[i] - 'a'];
}
g[b] = p;
w[b] = 0;
st[p].insert(0);
}
void ac(){ //建立fail树
queue<int> q;
for(int i = 0; i < 26; i++){
if(!tr[0][i]) continue;
fail[tr[0][i]] = 0;
q.push(tr[0][i]);
}
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = 0; i < 26; i++){
if(tr[u][i]){
fail[tr[u][i]] = tr[fail[u]][i];
q.push(tr[u][i]);
}
else tr[u][i] = tr[fail[u]][i];
}
}
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> s;
build(s, i);
}
ac();
for(int i = 1; i <= m; i++){
int mx = -1;
int opt, x, y;
cin >> opt;
if(opt == 1){ //修改操作
cin >> x >> y;
st[g[x]].insert(y); //插入当前值
auto flag = st[g[x]].find(w[x]);
if(flag != st[g[x]].end()) st[g[x]].erase(flag); //删除上一个值
w[x] = y; //更新当前值
}
else{
int u = 0, p;
cin >> t;
for(int j = 0, len = strlen(t); j < len; j++){
u = tr[u][t[j] - 'a'];
p = u;
while(p){
if(vis[p] == i) break;
vis[p] = i;
auto fg = st[p].end();
if(!st[p].empty()) {
fg--;
mx = max(mx, *fg);//找到最大值, 更新答案
}
p = fail[p];
}
}
cout << mx << endl;
}
}
return 0;
}

浙公网安备 33010602011771号