• [BZOJ 4556][Tjoi2016&Heoi2016]字符串


    [BZOJ 4556] 字符串

    题意

    原题面

    给定一个长度为 (n) 的串 (s), (m) 次查询 (s[a:b]) 的所有子串与 (s[c:d]) 的LCP的最大值.

    (1le n,mle1 imes 10^5)

    题解

    据说后缀数组挺好做的?

    管他呢反正垃圾rvalue只会用SAM做题(QAQ)

    首先SAM比较好搞的是子串公共后缀 (right集合出发的长度一定的后缀) , 于是我们把原串 std::reverse 一下再搞.

    翻转之后要做的就是查询 (s[a:b]) 的所有子串与 (s[c:d]) 的最长公共后缀的最大值.

    容易小于最优解的公共后缀对应的子串是可以构造出来的, 于是答案是可以二分的. 我们二分这个答案为 mid. 则我们要做的就是查询 (s[d-mid+1:d]) 是否是 (s[a:b]) 的子串.

    我们如果能够定位到 (s[d-mid+1:d]) 在SAM上运行后的状态, 那么显然这个状态的right集合的位置都是 (s[d-mid+1:d])(s) 中的出现位置的右端点. 于是只要right集合中有 ([a+mid-1,b]) 内的值则代表当前二分的答案合法.

    定位某个子串在SAM上运行后的状态的一般做法是在prt树上倍增. 查询 (s[l:r]) 的状态时, 从代表 (s[:r]) 的状态(这个可以在构造时存储)出发向上跳, 找到第一个 (len(p)ge r-l+1) 的状态 (p) 即可. (再向上跳必然会导致 (len(p)<r-l+1), 于是当前的 (p) 满足 (len(prt(p))<r-l+1le len(p)), 易知 (p) 包含 (s[l:r])).

    求right集合可以用线段树合并解决. 动态开点线段树合并的复杂度是有保证的 ((O(log n))).

    参考代码

    #include <bits/stdc++.h>
    
    const int MAXN=2e5+10;
    
    struct Node{
    	int l;
    	int r;
    	int sum;
    	Node* lch;
    	Node* rch;
    	Node(int,int,int=0);
    	void Insert(int);
    	int Query(int,int);
    };
    Node* N[MAXN];
    
    int n;
    int q;
    int cnt=1;
    int root=1;
    int last=1;
    int s[MAXN];
    int len[MAXN];
    int end[MAXN];
    char str[MAXN];
    int scnt[MAXN];
    int pprt[20][MAXN];
    int* prt=pprt[0];
    std::map<char,int> chd[MAXN];
    
    void BucSort();
    int Extend(char);
    inline void Rev(int&);
    Node* Merge(Node*,Node*);
    bool Check(int,int,int,int,int);
    
    int main(){
    	scanf("%d%d",&n,&q);
    	scanf("%s",str+1);
    	std::reverse(str+1,str+1+n);
    	for(int i=1;i<=n;i++){
    		end[i]=Extend(str[i]);
    		N[end[i]]=new Node(1,n);
    		N[end[i]]->Insert(i);
    	}
    	BucSort();
    	for(int i=cnt;i>=1;i--)
    		N[prt[s[i]]]=Merge(N[prt[s[i]]],N[s[i]]);
    	int lg;
    	for(lg=1;(1<<lg)<cnt;lg++)
    		for(int i=cnt;i>=1;i--)
    			pprt[lg][i]=pprt[lg-1][pprt[lg-1][i]];
    	while(q--){
    		int a,b,c,d;
    		scanf("%d%d%d%d",&a,&b,&c,&d);
    		Rev(a),Rev(b),Rev(c),Rev(d);
    		std::swap(a,b),std::swap(c,d);
    		int l=0,r=std::min(d-c+1,b-a+1)+1;
    		while(r-l>1){
    			int mid=(l+r)>>1;
    			int p=end[d];
    			for(int i=lg;i>=0;i--){
    				if(len[pprt[i][p]]>=mid)
    					p=pprt[i][p];
    			}
    			if(N[p]->Query(a+mid-1,b))
    				l=mid;
    			else
    				r=mid;
    		}
    		printf("%d
    ",l);
    	}
    	return 0;
    }
    
    void BucSort(){
    	memset(scnt,0,sizeof(scnt));
    	for(int i=1;i<=cnt;i++)
    		++scnt[len[i]];
    	for(int i=1;i<=cnt;i++)
    		scnt[i]+=scnt[i-1];
    	for(int i=cnt;i>=1;i--)
    		s[scnt[len[i]]--]=i;
    }
    
    int Extend(char x){
    	int p=last;
    	int np=++cnt;
    	last=np;
    	len[np]=len[p]+1;
    	while(p&&!chd[p].count(x))
    		chd[p][x]=np,p=prt[p];
    	if(!p)
    		prt[np]=root;
    	else{
    		int q=chd[p][x];
    		if(len[q]==len[p]+1)
    			prt[np]=q;
    		else{
    			int nq=++cnt;
    			len[nq]=len[p]+1;
    			chd[nq]=chd[q];
    			prt[nq]=prt[q];
    			prt[q]=nq;
    			prt[np]=nq;
    			while(p&&chd[p][x]==q)
    				chd[p][x]=nq,p=prt[p];
    		}
    	}
    	return np;
    }
    
    void Node::Insert(int x){
    	++this->sum;
    	if(l!=r){
    		int mid=(l+r)>>1;
    		if(x<=mid){
    			if(this->lch==NULL)
    				this->lch=new Node(l,mid);
    			this->lch->Insert(x);
    		}
    		else{
    			if(this->rch==NULL)
    				this->rch=new Node(mid+1,r);
    			this->rch->Insert(x);
    		}
    	}
    }
    
    Node* Merge(Node* a,Node* b){
    	if(a==NULL)
    		return b;
    	if(b==NULL)
    		return a;
    	Node* tmp=new Node(a->l,a->r,a->sum+b->sum);
    	tmp->lch=Merge(a->lch,b->lch);
    	tmp->rch=Merge(a->rch,b->rch);
    	return tmp;
    }
    
    Node::Node(int l,int r,int sum):l(l),r(r),sum(sum),lch(NULL),rch(NULL){}
    
    inline void Rev(int& x){
    	x=n-x+1;
    }
    
    int Node::Query(int l,int r){
    	if(l<=this->l&&this->r<=r)
    		return this->sum;
    	else{
    		int ans=0;
    		if(this->lch&&l<=this->lch->r)
    			ans+=this->lch->Query(l,r);
    		if(this->rch&&this->rch->l<=r)
    			ans+=this->rch->Query(l,r);
    		return ans;
    	}
    }
    
    

  • 相关阅读:
    spring AOP概述和简单应用
    log4j输出指定功能的log配置方式区别
    java项目配置常见问题
    android 浮动按钮的伸缩效果
    Android之探究viewGroup自定义子属性参数的获取流程
    javaWeb之maven多数据库环境的配置信息
    mybatis generator配置生成代码的问题
    java之初识服务器跨域获取数据
    java之Maven配置和springMvc的简单应用
    UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe5
  • 原文地址:https://www.cnblogs.com/rvalue/p/10380954.html
Copyright © 2020-2023  润新知