zoukankan      html  css  js  c++  java
  • [LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字

    [LOJ 2720][BZOJ 5417][UOJ 395][NOI 2018]你的名字

    题意

    给定一个大串 (S) 以及 (q) 次询问, 每次询问给定一个串 (T) 和区间 ([l,r]), 求 (T) 中有多少本质不同的子串不是 (S[l:r]) 的子串.

    (|S|le 5 imes 10^5,qle 10^5,sum|T|le10^6).

    题解

    普通的码农字符串题...

    获得成就: (40 exttt{min}(2400 exttt{s})) 内打完 (3.9 exttt{kB}) 的代码(然而并没有打完就A...还是太菜了...)

    感觉考场上如果T1T2没打满的话写个 (68) 分沙雕SAM暴力(询问区间都是原串的 (17) 个测试点)就可以跑路了...分高好写还不用调...

    个人的大体思路是: 因为求本质不同子串个数是容易的, 所以先补集转化为求 (T) 的所有本质不同的子串中是 (S[l:r]) 的串的个数.

    按照套路我们维护一个类似扫描线的东西, 用SAM对 (T) 的所有下标 (i) 求出以 (i) 为右端点且是 (S[l:r]) 的子串的最长子串长度. 按照SAM的套路, 这部分的计算就是直接用 (T)(S) 的SAM上面跑, 如果可以匹配就匹配, 不能匹配跳到后缀自动机的父亲节点上来移动左端点.

    按照上面这样计算是对整串来说的. 因为还要考虑区间 ([l,r]) 的事情, 我们用线段树合并维护出每个结点的right集合, 设当前匹配长度为 (len), 那么只有当当前状态的right集合与 ([l+len-1,r]) 有交集才说明与 (S[l:r]) 匹配. 如果不满足这个条件, 不能按照SAM的普通套路直接跳prt, 而是应该让 (len) 减少 (1). 直接跳的话左端点会移动若干个位置, 可能会跳过最优长度. SAM普通套路直接跳prt是因为如果 (len) 只减少 (1) 而没有到达prt的长度的话依然没有改变当前状态不能匹配的事实.

    然而直接这样计算铁定会有重复, 我们对 (T) 的反串建SA求出所有前缀的最长公共后缀长度作为去重的参考信息. 按照SA求本质不同子串个数的套路, 重复的子串必然出现在后缀数组上相邻的两个后缀(注意是反串的后缀)上. 假设相邻的两个后缀 (i,j) 的在原串的对应前缀的最大匹配长度分别是 (mlen_i,mlen_j) 且它们的LCP是 (height) 的话, 贡献就是 (mlen_j-min(height,mlen_i)).

    实际上 (mlen_j) 肯定不会小于 (min(height,mlen_i)), 因为这部分串是完全一样的. 所以直接减就行了.

    以前用map的写法被UOJ 64位指针debuff给卡内存了qaq...然而指针线段树依然在被卡内存...UOJ变97分了QAQ

    参考代码

    之前听说今天minusT要更新Subterranean Rose? 完蛋在学校看不了qaq

    #include <bits/stdc++.h>
    
    const int MAXN=1e6+10;
    typedef long long intEx;
    
    struct Node{
    	int l;
    	int r;
    	int sum;
    	Node* lch;
    	Node* rch;
    	Node(int,int);
    	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 SA[MAXN];
    int len[MAXN];
    int prt[MAXN];
    int buc[MAXN];
    int mlen[MAXN];
    char buf[MAXN];
    int rank[MAXN];
    int height[MAXN];
    int chd[MAXN][26];
    int* x=new int[MAXN];
    int* y=new int[MAXN];
    
    void BuildSAM();
    void Extend(char);
    void BuildSA(char*,int);
    Node* Merge(Node*,Node*);
    
    int main(){
    	freopen("name.in","r",stdin);
    	freopen("name.out","w",stdout);
    	scanf("%s",buf+1);
    	n=strlen(buf+1);
    	for(int i=1;i<=n;i++)
    		Extend(buf[i]);
    	BuildSAM();
    	scanf("%d",&q);
    	while(q--){
    		int l,r;
    		scanf("%s",buf+1);
    		scanf("%d%d",&l,&r);
    		int m=strlen(buf+1);
    		int cur=root,curlen=0;
    		for(int i=1;i<=m;i++){
    			int x=buf[i]-'a';
    			while(cur!=root&&!chd[cur][x]){
    				cur=prt[cur];
    				curlen=len[cur];
    			}
    			if(chd[cur][x]){
    				++curlen;
    				cur=chd[cur][x];
    				while(cur!=root&&!N[cur]->Query(l+curlen-1,r)){
    					--curlen;
    					if(curlen<=len[prt[cur]])
    						cur=prt[cur];
    				}
    			}
    			mlen[i]=curlen;
    		}
    		std::reverse(buf+1,buf+m+1);
    		BuildSA(buf,m);
    		intEx ans=0;
    		int last=0;
    		for(int i=1;i<=m;i++){
    			ans+=(m-SA[i]+1)-height[i];
    			last=std::min(height[i],last);
    			ans-=mlen[m-SA[i]+1]-last;
    			last=mlen[m-SA[i]+1];
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
    void BuildSAM(){
    	memset(buc,0,sizeof(int)*(n+1));
    	for(int i=1;i<=cnt;i++)
    		++buc[len[i]];
    	for(int i=1;i<=n;i++)
    		buc[i]+=buc[i-1];
    	for(int i=cnt;i>=1;i--)
    		s[buc[len[i]]--]=i;
    	for(int i=cnt;i>=1;i--)
    		N[prt[s[i]]]=Merge(N[prt[s[i]]],N[s[i]]);
    }
    
    void Extend(char ch){
    	int p=last;
    	int x=ch-'a';
    	int np=++cnt;
    	last=np;
    	len[np]=len[p]+1;
    	N[np]=new Node(1,n);
    	N[np]->Insert(len[np]);
    	while(p&&!chd[p][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;
    			memcpy(chd[nq],chd[q],sizeof(chd[q]));
    			N[nq]=new Node(1,n);
    			len[nq]=len[p]+1;
    			prt[nq]=prt[q];
    			prt[q]=nq;
    			prt[np]=nq;
    			while(p&&chd[p][x]==q)
    				chd[p][x]=nq,p=prt[p];
    		}
    	}
    }
    
    void Node::Insert(int x){
    	++this->sum;
    	if(this->l!=this->r){
    		int mid=(this->l+this->r)>>1;
    		if(x<=mid){
    			if(this->lch==NULL)
    				this->lch=new Node(this->l,mid);
    			this->lch->Insert(x);
    		}
    		else{
    			if(this->rch==NULL)
    				this->rch=new Node(mid+1,this->r);
    			this->rch->Insert(x);
    		}
    	}
    }
    
    int Node::Query(int l,int r){
    	if(l<=this->l&&this->r<=r)
    		return this->sum;
    	else{
    		int ans=0;
    		int mid=(this->l+this->r)>>1;
    		if(l<=mid&&this->lch)
    			ans+=this->lch->Query(l,r);
    		if(mid+1<=r&&this->rch)
    			ans+=this->rch->Query(l,r);
    		return ans;
    	}
    }
    
    Node* Merge(Node* a,Node* b){
    	if(a==NULL)
    		return b;
    	if(b==NULL)
    		return a;
    	Node* N=new Node(a->l,b->r);
    	N->sum=a->sum+b->sum;
    	N->lch=Merge(a->lch,b->lch);
    	N->rch=Merge(a->rch,b->rch);
    	return N;
    }
    
    void BuildSA(char* s,int n){
    	int m=127;
    	memset(buc+1,0,sizeof(int)*m);
    	for(int i=1;i<=n;i++)
    		++buc[x[i]=s[i]];
    	for(int i=1;i<=m;i++)
    		buc[i]+=buc[i-1];
    	for(int i=n;i>=1;i--)
    		SA[buc[x[i]]--]=i;
    	for(int k=1;k<n;k<<=1){
    		int p=0;
    		for(int i=n-k+1;i<=n;i++)
    			y[++p]=i;
    		for(int i=1;i<=n;i++)
    			if(SA[i]>k)
    				y[++p]=SA[i]-k;
    		memset(buc+1,0,sizeof(int)*m);
    		for(int i=1;i<=n;i++)
    			++buc[x[i]];
    		for(int i=1;i<=m;i++)
    			buc[i]+=buc[i-1];
    		for(int i=n;i>=1;i--)
    			SA[buc[x[y[i]]]--]=y[i];
    		std::swap(x,y);
    		x[SA[1]]=1;
    		p=1;
    		for(int i=2;i<=n;i++)
    			x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p;
    		if(p>=n)
    			break;
    		m=p;
    	}
    	for(int i=1;i<=n;i++)
    		rank[SA[i]]=i;
    	int k=0;
    	for(int i=1;i<=n;i++){
    		if(rank[i]==1)
    			continue;
    		if(k)
    			--k;
    		int j=SA[rank[i]-1];
    		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])
    			++k;
    		height[rank[i]]=k;
    	}
    }
    
    Node::Node(int l,int r):l(l),r(r),sum(0),lch(NULL),rch(NULL){}
    
    

  • 相关阅读:
    elasticsearch 索引清理脚本及常用命令
    git 快速入门及常见用法
    第01章-成本,你真的算对过吗?
    windows mysql安装及常用命令
    centos7 systemctl配置开机自启动服务
    python pip手动安装二进制包
    centos7使用nginx+uwsgi部署python django项目
    python json.loads()、json.dumps()和json.dump()、json.load()区别
    比阿里云快2倍的InfluxDB集群,我们开源了
    为什么是InfluxDB | 写在《InfluxDB原理和实战》出版之际
  • 原文地址:https://www.cnblogs.com/rvalue/p/10779929.html
Copyright © 2011-2022 走看看