[BJWC2018]Border 的四种求法,洛谷P4482,fail树上树链剖分

本文介绍了一种结合AC自动机与主席树的数据结构优化方法,用于解决字符串匹配问题中的边框查找任务。通过在AC自动机的fail树上进行跳跃,并利用主席树维护节点的right集合,实现了高效的查询和更新操作。文章详细阐述了如何使用重链剖分进一步优化查询复杂度,以及代码实现的细节。

正题

      首先很容易可以想到一个在fail树上跳的方法,先找到r的结束节点,向上跳,对于每一个祖先x,找最大的i,满足i<l+len[x]且i<r,所有这样的i取最大值,就可以知道border的最大右端点.

      但是这样每次操作是nlogn的,(也许可以优化一个根号),但时间复杂度依然很大.

      我们考虑令每次这样的询问与len[x]无关,也就是变成i-len[x]<l且i<r,一眼就可以知道这个东西也可以主席树维护,维护一个以i为下标,i-len[x]为权值的线段树即可.

      我们先用线段树合并维护出每个点的right集合.

      接着,我们考虑使用重链剖分来优化这个过程,对于一个点x,同时维护重链剖分上的一棵主席树,主席树中插入的是自身的right和轻儿子的right,为什么不插重儿子的right?因为跳上来的时候会在当前节点暴力算一遍子树的答案(这里的暴力指的是第一行的线段树维护)我们们只需要知道当前节点到这条重链顶端的轻儿子和自身答案即可.

      由于重链上的线段树与len是无关的,询问的时候无区别,重点就是运用了这个性质.

      代码还是比较恶心的,打+调差不多两个小时.

      可以仔细研究一下代码在算答案的时候为什么是一个log?

#pragma GCC optimize("O2")
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;

const int N=400010;
int T,ls[20000010],rs[20000010],mmin[20000010],rt[N][2],n;//全 重
int ans;
struct edge{
	int y,nex;
}e[N<<1]; 
int first[N],len=0;
void insq(int x,int y){e[++len]=(edge){y,first[x]};first[x]=len;}
void insert(int&now,int x,int l=1,int r=n){//全 插入
	now=++T;
	if(l==r) return ;
	int mid=(l+r)/2;
	if(x<=mid) insert(ls[now],x,l,mid);
	else insert(rs[now],x,mid+1,r);
}

int merge(int x,int y){//全 合并
	if(!x || !y) return x+y;
	int now=++T;
	ls[now]=merge(ls[x],ls[y]);
	rs[now]=merge(rs[x],rs[y]);
	return now;
}

void ins(int&now,int las,int x,int t,int l=1,int r=n){//重 插入
	now=++T;
	if(l==r) {mmin[now]=min(t,mmin[las]);return ;}
	int mid=(l+r)/2;
	if(x<=mid) rs[now]=rs[las],ins(ls[now],ls[las],x,t,l,mid);
	else ls[now]=ls[las],ins(rs[now],rs[las],x,t,mid+1,r);
	mmin[now]=min(mmin[ls[now]],mmin[rs[now]]);
}

void gi(int now,int&x,int y,int l=1,int r=n){//将 全 中的now,插入 重 中的x中
	if(now==0) return ;
	if(l==r) {ins(x,x,l,l-y);return ;}
	int mid=(l+r)/2;
	gi(ls[now],x,y,l,mid);
	gi(rs[now],x,y,mid+1,r);
}

int ga(int now,int x,int y,int t,int l=1,int r=n){//重 中mmin[i]<x的最大的i
	if(mmin[now]>=t) return 0;
	if(l==r) return r;
	int mid=(l+r)/2;
	if(y<=mid) return ls[now]?ga(ls[now],x,y,t,l,mid):0;
	else if(mid<x) return rs[now]?ga(rs[now],x,y,t,mid+1,r):0;
	else{
		int tmp=rs[now]?ga(rs[now],mid+1,y,t,mid+1,r):0;
		if(tmp) return tmp;
		return ls[now]?ga(ls[now],x,mid,t,l,mid):0;
	}
}

int gas(int now,int x,int y,int l=1,int r=n){//全 中x到y中最大的i
	if(l==r) return l;
	int mid=(l+r)/2;
	if(y<=mid) return ls[now]?gas(ls[now],x,y,l,mid):0;
	else if(mid<x) return rs[now]?gas(rs[now],x,y,mid+1,r):0;
	else{
		int tmp=rs[now]?gas(rs[now],mid+1,y,mid+1,r):0;
		if(tmp) return tmp;
		return ls[now]?gas(ls[now],x,mid,l,mid):0;
	}
}

struct Suffix_AutoMaton{
	char s[N];
	int len[N],tot,las,ch[N][26],sum[N],a[N],ed[N],rig[N];
	int fa[N],top[N],son[N],op[N];
	void extend(int pos,int c){
		int x=++tot,p=las;len[x]=len[p]+1;las=x;ed[pos]=x;rig[x]=pos;
		insert(rt[x][0],pos);
		while(p!=-1 && !ch[p][c]) ch[p][c]=x,p=fa[p];
		if(p==-1) return ;
		if(len[ch[p][c]]==len[p]+1) fa[x]=ch[p][c];
		else{
			int q=++tot,tmp=ch[p][c];len[q]=len[p]+1;
			fa[q]=fa[tmp];fa[tmp]=fa[x]=q;
			for(int i=0;i<26;i++) ch[q][i]=ch[tmp][i];
			while(p!=-1 && ch[p][c]==tmp) ch[p][c]=q,p=fa[p];
		}
	}
	void build(){
		scanf("%s",s+1);n=strlen(s+1);fa[0]=-1;mmin[0]=1e9;
		for(int i=1;i<=n;i++) extend(i,s[i]-'a');
		for(int i=1;i<=tot;i++) sum[len[i]]++,insq(fa[i],i);
		for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
		for(int i=1;i<=tot;i++) a[sum[len[i]]--]=i;
	}
	void dfs(int x,int tp){
		top[x]=tp;
		if(rig[x]) ins(rt[x][1],rt[x][1],rig[x],0);
		for(int i=first[x];i!=0;i=e[i].nex) if(e[i].y!=son[x])
			gi(rt[e[i].y][0],rt[x][1],len[x]),dfs(e[i].y,e[i].y);
		if(son[x]) rt[son[x]][1]=rt[x][1],dfs(son[x],tp);
	}
	void prepare(){
		for(int i=tot;i>=1;i--) if(fa[a[i]]){
			int x=a[i];
			op[x]++;op[fa[x]]+=op[x];
			if(op[x]>op[son[fa[x]]]) son[fa[x]]=x;
			rt[fa[x]][0]=merge(rt[fa[x]][0],rt[x][0]);
		}
		for(int i=first[0];i!=0;i=e[i].nex) dfs(e[i].y,e[i].y);
	}
	void solve(){
		int q,l,r;
		scanf("%d",&q);
		while(q--){
			scanf("%d %d",&l,&r);
			int now=ed[r];ans=0;
			while(now!=0){
				if(r-1){
					ans=max(ans,rt[now][0]?gas(rt[now][0],1,min(l+len[now]-1,r-1)):0);
					ans=max(ans,rt[now][1]?ga(rt[now][1],1,r-1,l):0);
				}
				now=fa[top[now]];
			}
			printf("%d\n",max(ans-l+1,0));
		}
	}
}SAM;

int main(){
	SAM.build();SAM.prepare();SAM.solve();
}

 

(Kriging_NSGA2)克里金模型结合多目标遗传算法求最优因变量及对应的最佳自变量组合研究(Matlab代码实现)内容概要:本文介绍了克里金模型(Kriging)与多目标遗传算法NSGA-II相结合的方法,用于求解最优因变量及其对应的最佳自变量组合,并提供了完整的Matlab代码实现。该方法首先利用克里金模型构建高精度的代理模型,逼近复杂的非线性系统响应,减少计算成本;随后结合NSGA-II算法进行多目标优化,搜索帕累托前沿解集,从而获得多个最优折衷方案。文中详细阐述了代理模型构建、算法集成流程及参数设置,适用于工程设计、参数反演等复杂优化问题。此外,文档还展示了该方法在SCI一区论文中的复现应用,体现了其科学性与实用性。; 适合人群:具备一定Matlab编程基础,熟悉优化算法和数值建模的研究生、科研人员及工程技术人员,尤其适合从事仿真优化、实验设计、代理模型研究的相关领域工作者。; 使用场景及目标:①解决高计算成本的多目标优化问题,通过代理模型降低仿真次数;②在无法解析求导或函数高度非线性的情况下寻找最优变量组合;③复现SCI高水平论文中的优化方法,提升科研可信度与效率;④应用于工程设计、能源系统调度、智能制造等需参数优化的实际场景。; 阅读建议:建议读者结合提供的Matlab代码逐段理解算法实现过程,重点关注克里金模型的构建步骤与NSGA-II的集成方式,建议自行调整测试函数或实际案例验证算法性能,并配合YALMIP等工具包扩展优化求解能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值