• P6640 [BJOI2020] 封印(后缀自动机+线段树+二分)


    首先对每个位置处理出它作为右端点的最长子序列zz[i]

    这个问题是老题

    然后,每次区间询问就是求

    max(i-max(l,i-zz[i]+1)+1)

    这里注意到,i-zz[i]+1这玩意好像是单调不减的?

    尝试证一下...

    那么可以二分区间内第一个i-zz[i]+1大于l的位置p

    p前面半段的答案稳定

    p后面半段的答案用线段树维护zz[i]的最大值即可

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6;
    int len[maxn],link[maxn],nxt[maxn][26];
    int sz[maxn];
    int tot=1,lst=1;
    string s,t;
    int n;
    void sam_extend (char c) {
    	int cur=++tot;
    	len[cur]=len[lst]+1;
    	sz[cur]=1;
    	int p=lst;
    	while (p&&!nxt[p][c-'a']) {
    		nxt[p][c-'a']=cur;
    		p=link[p];
    	}
    	if (!p) {
    		link[cur]=1;
    	}
    	else {
    		int q=nxt[p][c-'a'];
    		if (len[p]+1==len[q]) {
    			link[cur]=q;
    		}
    		else {
    			int clone=++tot;
    			len[clone]=len[p]+1;
    			for (int i=0;i<26;i++) {
    				nxt[clone][i]=nxt[q][i];
    			}
    			link[clone]=link[q];
    			while (p&&nxt[p][c-'a']==q) {
    				nxt[p][c-'a']=clone;
    				p=link[p];
    			}
    			link[q]=link[cur]=clone;
    		}
    	}
    	lst=cur;
    }
    vector<int> g[maxn];
    int z[maxn];
    int zz[maxn];
    int c[maxn];
    void build (int i,int l,int r) {
    	if (l==r) {
    		c[i]=z[l];
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    	c[i]=max(c[i<<1],c[i<<1|1]);
    }
    int query (int i,int l,int r,int L,int R) {
    	if (l>=L&&r<=R) return c[i];
    	int mid=(l+r)>>1;
    	int ans=0;
    	if (L<=mid) ans=max(ans,query(i<<1,l,mid,L,R));
    	if (R>mid) ans=max(ans,query(i<<1|1,mid+1,r,L,R));
    	return ans;
    } 
    int main () {
    	cin>>s>>t;
    	n=s.size();
    	for (char i:t) {
    		sam_extend(i);
    	} 
    	int v=1,l=0;
    	for (int i=0;i<s.size();i++) {
    		while (v&&!nxt[v][s[i]-'a']) {
    			v=link[v];
    			l=len[v];
    		} 
    		if (nxt[v][s[i]-'a']) {
    			v=nxt[v][s[i]-'a'];
    			l++;
    		}
    		z[i+1]=l;
    		zz[i+1]=i+1-l+1;
    	}
    	//for (int i=1;i<=n;i++) printf("%d ",z[i]);
    	build(1,1,n);
    	int q;
    	cin>>q;
    	while (q--) {
    		int l,r;
    		scanf("%d%d",&l,&r);
    		int L=l,R=r,p=-1;
    		while (L<=R) {
    			int mid=(L+R)>>1;
    			if (zz[mid]>l) {
    				p=mid;
    				R=mid-1;
    			}
    			else {
    				L=mid+1;
    			}
    		}
    		int ans;
    		//printf("%d ",p);
    		if (p!=-1)
    			ans=max(p-l,query(1,1,n,p,r));
    		else
    			ans=r-l+1;
    		printf("%d
    ",ans);
    	}
    }
  • 相关阅读:
    数据特征分析:5.相关性分析
    数据特征分析:4.正态分布与正态性检验
    go-文件操作
    图-迪杰斯特拉算法
    图-克鲁斯卡尔算法
    图-普利姆算法
    go-客户信息关系系统
    go-家庭收支记账软件例子
    采用邻接表表示图的深度优先搜索遍历
    广度优先搜索遍历连通图
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15017603.html
Copyright © 2020-2023  润新知