ac自动机fail树上dfs序建可持久化线段树

#include <cstring>
#include <cstdio>
#define max(a,b) a>b?a:b
const int mn=300005;
struct Trie{
	int father,fail,next[26];
	void init(){
		father=fail=0;
		memset(next,0,sizeof next);
	}
}tree[mn];
struct Xls{
	int size,y[mn],nxt[mn],fst[mn];
	void set(){
		size=0;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		y[++size]=b;
		nxt[size]=fst[a];
		fst[a]=size;
	}
}g;
struct Segtree{
	int val,add,l,r;
}segtree[mn*4];
char str[mn];
int pos[mn],n,cnt,val[21000],in[mn],out[mn];
void build(char ch[]){
	int rt=1,t,len=strlen(ch);
	for (int i=0;i<len;i++){
		t=ch[i]-'a';
		if (!tree[rt].next[t]){
			tree[++cnt].init();
			tree[rt].next[t]=cnt;
		}
		tree[tree[rt].next[t]].father=rt;
		rt=tree[rt].next[t];
	}
}
int q[310000];
void build_AC(){
	int head=0,tail=0,rt,son,k;
	q[++tail]=1;tree[0].fail=1;
	while (head<tail){
		rt=q[++head];
		for (int i=0;i<26;i++){
			son=tree[rt].next[i];
			if (!son){
				tree[rt].next[i]=tree[tree[rt].fail].next[i];
				continue;
			}
			k=tree[rt].fail;
			while (!tree[k].next[i])
				k=tree[k].fail;
			tree[son].fail=tree[k].next[i];
			q[++tail]=son;
		}
	}
	g.set();
	for (int i=1;i<=cnt;i++)
		g.add(tree[i].fail,i);
}
int count_dfs;
void dfs(int rt){
	in[rt]=++count_dfs;
	for (int i=g.fst[rt];i;i=g.nxt[i])
		dfs(i);
	out[rt]=count_dfs;
}
void buildseg(int rt,int le,int ri){
	segtree[rt].val=segtree[rt].add=0;
	segtree[rt].l=le;segtree[rt].r=ri;
	if (le==ri)
		return;
	buildseg(rt<<1,le,(le+ri)>>1);
	buildseg(rt<<1|1,((le+ri)>>1)+1,ri);
}
void pushdown(int rt){
	if (segtree[rt].add){
		segtree[rt<<1].add=max(segtree[rt<<1].add,segtree[rt].add);
		segtree[rt<<1].val=max(segtree[rt<<1].val,segtree[rt].add);
		segtree[rt<<1|1].add=max(segtree[rt<<1|1].add,segtree[rt].add);
		segtree[rt<<1|1].val=max(segtree[rt<<1|1].val,segtree[rt].add);
		segtree[rt].add=0;
	}
}
int query(int rt,int x){
	if (segtree[rt].l==segtree[rt].r)
		return segtree[rt].val;
	pushdown(rt);
	if (x<=segtree[rt<<1].r)
		return query(rt<<1,x);
	else
		return query(rt<<1|1,x);
}
int XLE,XRI,Xval;
void update(int rt){
	if (segtree[rt].l>XRI||segtree[rt].r<XLE)
		return;
	if (XLE<=segtree[rt].l&&segtree[rt].r<=XRI){
		segtree[rt].val=max(segtree[rt].val,Xval);
		segtree[rt].add=max(segtree[rt].add,Xval);
		return;
	}
	pushdown(rt);
	update(rt<<1);
	update(rt<<1|1);
	segtree[rt].val=max(segtree[rt<<1].val,segtree[rt<<1|1].val);
}
int solve(){
	int rt,t,big,result=0;
	for (int i=1;i<=n;i++){
		rt=1;big=0;
		for (int j=pos[i-1];j<pos[i];j++){
			t=str[j]-'a';
			rt=tree[rt].next[t];
			big=max(big,query(1,in[rt]));
		}
		XLE=in[rt];XRI=out[rt];Xval=big+val[i];
		update(1);
		result=max(result,Xval);
	}
	return result;
}
int main(){
	int T;
	scanf("%d",&T);
	for (int Case=1;Case<=T;Case++){
		memset(str,0,sizeof str);
		memset(pos,0,sizeof pos);
		cnt=1;
		tree[0].init();
		tree[1].init();
		for (int i=0;i<26;i++)
			tree[0].next[i]=1;
		scanf("%d",&n);
		for (int i=1;i<=n;i++){
			scanf("%s%d",str+pos[i-1],&val[i]);
			build(str+pos[i-1]);
			pos[i]=pos[i-1]+strlen(str+pos[i-1]);
		}
		build_AC();
		count_dfs=0;
		dfs(1);
		buildseg(1,1,count_dfs);
		printf("Case #%d: %d\n",Case,solve());
	}
	return 0;
}

### 使用可持久化线段树维护AC自动机Fail树 对于涉及大量字符串匹配以及动态更新的问题,使用AC自动机模式串集合是一种高效的方法。当需要支持历史版本查询时,可以结合可持久化数据结构如可持久化线段树来增强功能。 #### 构AC自动机Fail树 首先构标准的AC自动机[^2]。在完成所有模式串插入后,通过广度优先搜索(BFS)计算每个节点沿`fail`指针形成的树形结构——即所谓的Fail树。该树允许快速定位到某个状态失败转移后的最近公共祖先位置。 #### 可持久化线段树的作用 为了记录不同时间点的状态变化情况,采用可持久化线段树存储每一步操作的结果。每当有新字符串加入或已有字符串被移除时,都会触发一次新的版本创过程: - **新增加字符串**:遍历其对应路径上的所有节点,并在线段树相应区间内增加计数值; - **删除已存在的字符串**:同样沿着受影响区域调整权重值; 这种设计使得即使经过多次修改之后仍然能够回溯至任意时刻的数据分布状况。 #### 实现细节 以下是具体实现的关键部分代码片段: ```cpp struct Node { int ch[ALPHABET_SIZE]; // 子结点数组 int fail; // 失败指针 }; vector<Node> ac_automaton; int root, node_count; // 插入函数... void insert(const string &pattern) { ... } // 计算fail指针... void build_fail() { ... } ``` 接着定义用于管理多个版本之间差异性的辅助类: ```cpp class PersistentSegmentTree { private: struct TreeNode { int sum_value; TreeNode *left_child, *right_child; ~TreeNode(){ delete left_child; delete right_child; } }; public: vector<TreeNode*> versions; void update(int version_id, int pos, int delta){ auto current_root = clone_tree(versions.back()); modify(current_root, 1, maxn, pos, delta); versions.push_back(current_root); } }; ``` 最后,在处理实际问题时调用上述组件即可达成目标需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倾海、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值