题目大意: 一开始有 n 个串,每个串有一个初始值
v
i
v_i
vi,注意串可能会相同。接下来有
m
m
m 个操作,每个操作有两种类型:
类型一:输入串
t
t
t,若
n
n
n 个串中有串是
t
t
t 的子串,输出这些串的权值的最大值,否则输出
−
1
-1
−1
类型二:修改第
x
x
x 串的值为
y
y
y
1 ≤ n , m ≤ 300000 1 \leq n,m \leq 300000 1≤n,m≤300000
要知道哪些串是 t t t 的子串,显然可以借助AC自动机,通过暴力跳 f a i l fail fail 链可以找到所有 t t t 的子串,并找到这些串的权值的最大值,但复杂度是 O ( ∣ t ∣ ∗ ∣ ∑ ∣ s ∣ ∣ ) O(|t|*|\sum|s||) O(∣t∣∗∣∑∣s∣∣)的。
如果熟悉 AC自动机的话会发现 f a i l fail fail 链实际是一棵 f a i l fail fail 树,暴力跳链的过程就是暴力爬链爬到根,既然是树链查询,很容易想到套用树链剖分。
对相同的串,它们在 trie 上的节点相同,用一个 multiset 存下这些串的权值,将最大值维护到线段树上。
无脑大力数据结构,复杂度上限为 O ( n log 2 n ) O(n \log^2n) O(nlog2n)。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 10;
int n, m, ans[maxn], p[maxn], dfn[maxn], top[maxn], son[maxn], f[maxn], sz[maxn], dep[maxn], N, tim, val[maxn];
vector<int> g[maxn];
multiset<int> st[maxn];
namespace seg_tree {
#define lson rt << 1, l, mid
#define rson rt << 1 | 1,mid + 1, r
int val[maxn << 2], sum[maxn << 2];
void build(int rt,int l,int r) {
sum[rt] = val[rt] = 0;
if (l == r) return ;
int mid = l + r >> 1;
build(lson); build(rson);
}
void update(int p,int v,int rt,int l,int r) {
if (l == r) {
val[rt] = v;
return ;
}
int mid = l + r >> 1;
if (p <= mid) update(p,v,lson);
else update(p,v,rson);
val[rt] = max(val[rt << 1],val[rt << 1 | 1]);
}
void modify(int p,int v,int rt,int l,int r) {
if (l == r) {
sum[rt] += v;
return ;
}
int mid = l + r >> 1;
if (p <= mid) modify(p,v,lson);
else modify(p,v,rson);
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
int qry_val(int L,int R,int rt,int l,int r) {
if (L <= l && r <= R) return val[rt];
int mid = l + r >> 1, ans = 0;
if (L <= mid) ans = max(ans,qry_val(L,R,lson));
if (mid + 1 <= R) ans = max(ans,qry_val(L,R,rson));
return ans;
}
int qry_sum(int L,int R,int rt,int l,int r) {
if (L <= l && r <= R) return sum[rt];
int mid = l + r >> 1, ans = 0;
if (L <= mid) ans += qry_sum(L,R,lson);
if (mid + 1 <= R) ans += qry_sum(L,R,rson);
return ans;
}
int query(int u) {
int res = 0;
while (top[u] != top[1]) {
res = max(res,qry_val(dfn[top[u]],dfn[u],1,1,N));
u = f[top[u]];
}
res = max(res,qry_val(dfn[1],dfn[u],1,1,N));
return res;
}
int ask(int u) {
int res = 0;
while (top[u] != top[1]) {
res += qry_sum(dfn[top[u]],dfn[u],1,1,N);
u = f[top[u]];
}
res += qry_sum(dfn[1],dfn[u],1,1,N);
return res;
}
}
namespace AC_trie {
struct node {
int son[26], fail, flag; //fail是该节点的父节点找不到该节点时的指向
void clear() {
memset(son,0,sizeof son);
flag = fail = 0;
}
} trie[maxn];
int cnt; //1号点和 0号点都是虚点,1号是根,0 是 1 的父亲,仅为了方便处理
void init() {
for (int i = 0; i <= cnt; i++) trie[i].clear();
cnt = 1;
}
void insert(char *s,int num) {
int u = 1, len = strlen(s);
for (int i = 0; i < len; i++) {
int v = s[i] - 'a';
if (!trie[u].son[v]) trie[u].son[v] = ++cnt;
u = trie[u].son[v];
}
//trie[u].flag = num; //标记这个节点到根的路径是第 num 个字符串
p[num] = u;
st[u].insert(val[num]);
//flag 可以存该节点的字符串数量,该节点的字符串等等信息,通常用来标记该节点是字符串
}
void getFail() {
queue<int> q;
for (int i = 0; i < 26; i++) trie[0].son[i] = 1;
trie[1].fail = 0; q.push(1);
while (!q.empty()) {
int u = q.front(); q.pop();
int Fail = trie[u].fail;
for (int i = 0; i < 26; i++) {
int v = trie[u].son[i];
if (!v) {
trie[u].son[i] = trie[Fail].son[i];
continue;
}
trie[v].fail = trie[Fail].son[i];
q.push(v);
}
}
}
ll query(char *s) { //查询文本串 s 能匹配多少个 模式串
int u = 1, len = strlen(s), siz = 0, ans = 0;
for (int i = 0; i < len; i++) {
int v = s[i] - 'a';
u = trie[u].son[v];
if (u >= 1) ans = max(ans,seg_tree :: query(u)), siz += seg_tree :: ask(u);
}
return siz ? ans : -1;
}
}
void prework(int u) {
sz[u] = 1; son[u] = 0;
for (auto v : g[u]) {
f[v] = u;
dep[v] = dep[u] + 1;
prework(v);
sz[u] += sz[v];
if (!son[u] || sz[v] > sz[son[u]])
son[u] = v;
}
}
void dfs(int u,int tp) {
dfn[u] = ++tim; top[u] = tp;
if (son[u]) dfs(son[u],tp);
for (auto v : g[u]) {
if (v != son[u]) dfs(v,v);
}
}
char s[maxn], t[maxn];
int main() {
scanf("%d%d",&n,&m);
AC_trie :: init();
for (int i = 1; i <= n; i++)
scanf("%s",s), AC_trie :: insert(s,i);
AC_trie :: getFail();
N = AC_trie :: cnt;
for (int i = 1; i <= N; i++) {
int u = AC_trie :: trie[i].fail;
g[u].push_back(i);
}
prework(1); dfs(1,1);
seg_tree :: build(1,1,N);
for (int i = 1; i <= N; i++)
seg_tree :: modify(dfn[i],st[i].size(),1,1,N);
for (int i = 1; i <= m; i++) {
int op, x, y; scanf("%d",&op);
if (op == 2) {
scanf("%s",t);
printf("%d\n",AC_trie :: query(t));
} else {
scanf("%d%d",&x,&y);
int v = p[x];
st[v].erase(st[v].find(val[x]));
st[v].insert(y);
val[x] = y;
seg_tree :: update(dfn[v],*st[v].rbegin(),1,1,N);
}
}
return 0;
}