zoukankan      html  css  js  c++  java
  • LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增

    题面:

     https://loj.ac/problem/2720


    考虑枚举T串的每个后缀i,我们要做两件事。

    一、统计有多少子串[i,j]在S中要求位置出现。

    二、去重。

    第二步好做,相当于在后缀数组上找到后继,假设后继的height为p。

    那对于i这个后缀,只计算i+p后面的即可。

    一的话每次找到最远的能匹配的一个位置,可以发现枚举每个后缀时右端点单调,双指针扫一下。

    剩下的就是快速的判定一个子串是否合法。

    考虑在后缀数组上左右二分到一个区间满足所有后缀和i的lcp都大于等于len(len=j-i+1)。

    判断这段区间是否存在一个位置,刚好在S中[l,r-len+1]的位置上。主席树直接做。

    重复一下做法:

    对所有的串建立后缀数组,同时求出height。

    在所有S串的地方主席树上更新。

    双指针扫一遍,check的时候左右二分找区间,主席树上查即可。

    这里补充一个技巧...因为区间可能不会很长,所以用倍增会比直接二分快很多....

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    #define N 1700000
    #define M 100050
    #define _max(x,y) ((x)>(y)?(x):(y))
    #define _min(x,y) ((x)<(y)?(x):(y))
    typedef long long ll;
    int n,Q,r[N],ln,be[M],ed[M],m;
    int wa[N],wb[N],ws[N],wv[N],sa[N],Rank[N],height[N],ql[M],qr[M],ppp[N],pos[N],lst[M];
    int f[22][N],Lg[N],t[N*10],ls[N*10],rs[N*10],root[N],cnt;
    inline char nc() {
    	static char buf[100000],*p1,*p2;
    	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    int rd() {
    	int x=0; char s=nc();
    	while(s<'0'||s>'9') s=nc();
    	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
    	return x;
    }
    char pbuf[100000],*pp=pbuf;
    void push(const char c) {
        if(pp-pbuf==100000) fwrite(pbuf,1,100000,stdout),pp=pbuf;
        *pp++=c;
    }
    void write(ll x) {
        static int sta[70];
        int top=0;
        do{sta[top++]=x%10,x/=10;}while(x);
        while(top) push(sta[--top]+'0');
    }
    void build_suffix_array() {
    	int i,j,p,*x=wa,*y=wb,*t;
    	m=32;
    	for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
        for(p=j=1;p<n;j<<=1,m=p) {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]-j>=0) y[p++]=sa[i]-j;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;i++) {
                if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=p-1;
                else x[sa[i]]=p++;
            }
        }
    	for(i=1;i<n;i++) Rank[sa[i]]=i;
        for(i=p=0;i<n-1;height[Rank[i++]]=p)
            for(p?p--:0,j=sa[Rank[i]-1];r[i+p]==r[j+p];p++);
    	for(Lg[0]=-1,i=1;i<n;i++) f[0][i]=height[i],Lg[i]=Lg[i>>1]+1;
    	for(i=1;(1<<i)<=n;i++) {
    		for(j=1;j+(1<<i)-1<n;j++) {
    			f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    		}
    	}
    }
    int get_min(int x,int y) {
    	int len=Lg[y-x+1],a=f[len][x],b=f[len][y-(1<<len)+1];
    	return _min(a,b);
    }
    int lcp(int x,int y) {
    	x=Rank[x],y=Rank[y];
    	if(x>y) swap(x,y);
    	return get_min(x+1,y);
    }
    void insert(int l,int r,int v,int x,int &y) {
    	y=++cnt; t[y]=t[x]+1;
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	if(v<=mid) rs[y]=rs[x],insert(l,mid,v,ls[x],ls[y]);
    	else ls[y]=ls[x],insert(mid+1,r,v,rs[x],rs[y]);
    }
    int Qx,Qy,ok;
    void query(int l,int r,int x,int y) {
    	if(t[x]==t[y]||ok) return ;
    	if(Qx<=l&&Qy>=r) {ok=1; return ;}
    	int mid=(l+r)>>1;
    	if(Qx<=mid) query(l,mid,ls[x],ls[y]);
    	if(Qy>mid) query(mid+1,r,rs[x],rs[y]);
    }
    bool check(int i,int j,int q) {
    	int len=j-i+1; Qx=ql[q],Qy=qr[q];
    	if(len>Qy-Qx+1) return 0;
    	int p=Rank[i];
    	int L=p,R=p,d;
    	for(d=1;p>d&&get_min(p-d+1,p)>=len;d<<=1);
    	for(d>>=1;d;d>>=1) if(get_min(L-d+1,p)>=len) L-=d;
    	for(d=1;p+d<n&&get_min(p+1,p+d)>=len;d<<=1);
    	for(d>>=1;d;d>>=1) if(get_min(p+1,R+d)>=len) R+=d;
    	Qy=Qy-len+1;
    	ok=0;
    	query(0,ln-1,root[L-1],root[R]);
    	return ok;
    }
    int main() {
    	char s=nc();
    	while(s<'a'||s>'z') s=nc();
    	register int i,j;
    	for(;s>='a'&&s<='z';s=nc()) {
    		r[ln++]=s-'a'+1;
    	}
    	n=ln; r[n++]=27;
    	Q=rd();
    	for(i=1;i<=Q;i++) {
    		be[i]=n;
    		s=nc();
    		while(s<'a'||s>'z') s=nc();
    		for(;s>='a'&&s<='z';s=nc()) {
    			pos[n]=i; r[n++]=s-'a'+1;
    		}
    		ed[i]=n-1;
    		ql[i]=rd()-1; qr[i]=rd()-1;
    		if(i!=Q) r[n++]=27;
    	}
    	r[n++]=0;
    	build_suffix_array();
    	for(i=n-1;i>0;i--) {
    		int p=sa[i],u=pos[p];
    		if(!u) continue;
    		if(lst[u]) ppp[p]=lcp(p,lst[u]);
    		lst[u]=p;
    	}
    	for(i=1;i<n;i++) {
    		int p=sa[i];
    		root[i]=root[i-1];
    		if(p>=0&&p<ln) insert(0,ln-1,p,root[i],root[i]);
    	}
    	for(i=1;i<=Q;i++) {
    		ll ans=0;
    		int k=be[i];
    		for(j=be[i];j<=ed[i];j++) {
    			if(k<j) k=j;
    			while(k<=ed[i]&&check(j,k,i)) k++;
    			k--;
    			ans+=ed[i]-j+1-_max(k-j+1,ppp[j]);
    		}
    		write(ans); push('
    ');
    	}
    	fwrite(pbuf,1,pp-pbuf,stdout);
    }
    
  • 相关阅读:
    OpenWrt的luci web管理器添加新菜单
    Lua基础
    2016年1月25日 《1024伐木累》-小白篇之开发网站,三天!(中篇-2奇怪的IE)-总章节十一
    《1024伐木累》-程序员妹子与花木兰
    《1024伐木累》-关注女神小号,藏大钱
    《1024伐木累》-小白篇之开发网站,三天!(中篇-1)-总章节十
    《1024伐木累》-炒股赚钱,大保健
    《1024伐木累》-找规律,女生不讲卫生~
    微信支付开发-Senparc.Weixin.MP详解
    《1024伐木累》-小白篇之开发网站,三天!(前篇)-总章节八
  • 原文地址:https://www.cnblogs.com/suika/p/9426839.html
Copyright © 2011-2022 走看看