• BZOJ4556: [Tjoi2016&Heoi2016]字符串


    BZOJ4556: [Tjoi2016&Heoi2016]字符串

    Description

    佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
    一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
    O,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公
    共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

    Input

    输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来
    m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,
    字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n
     

    Output

     对于每一次询问,输出答案。

    Sample Input

    5 5
    aaaaa
    1 1 1 5
    1 5 1 1
    2 3 2 3
    2 4 2 3
    2 3 2 4

    Sample Output

    1
    1
    2
    2
    2

    题解Here!
    首先看到最长公共前缀就知道,又是一道后缀数组。。。

    设我们的答案为$mid$,那么我们会发现一个很有趣的事实:如果$mid$可行的话,那么任意一个比$mid$小的数也可行。

    也就是说,问题满足可二分性,那么我们可以二分答案,将原问题转化为一个判定性问题:$mid$这个答案行不行?

    那么我们发现,如果$mid$这个答案可以的话,就会存在一个后缀$S$,

    1. 它的开头在$[a,b-mid+1]$当中。
    2. $lcp(S,c)>=mid$。

    再次转化一步,就是询问满足以上两个条件的后缀$S$的个数。

    经典的二元限制统计问题。。。

    我们的思路很简单,摁死一个再去管下一个,发现一件有趣的事实:

    如果把这些后缀排好序,那么$lcp$符合要求的一定是一段连续的区间。

    为什么?

    因为我们发现排好序以后,$lcp$这个函数是单峰的,并且峰值在自己这里。

    那么我们似乎可以二分左端点和右端点,需要$O(1)$求出区间最小值,直接套上$ST$表。

    那么最后我们发现现在两个限制都是区间型的了,而且是静态区间,没有修改,所以可以用主席树查询一发。

    复杂度是$O(nlog_2^2n)$。

    然后就光荣地在$BZOJ$上被卡常了。。。

    $UPDATE$:经过某些玄学优化,终于在$8s$内过了。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #define MAXN 100010
    using namespace std;
    int n,m;
    int root[MAXN];
    char str[MAXN];
    int top,sa[MAXN],rk[MAXN],tax[MAXN],tp[MAXN],height[MAXN],f[MAXN][20],Log[MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    namespace CT{
    	int size=0;
    	struct Chairman_Tree{
    		int l,r,sum;
    	}a[MAXN*20];
    	inline void buildtree(){
    		root[0]=a[0].l=a[0].r=a[0].sum=0;
    	}
    	void insert(int k,int v,int l,int r,int &rt){
    		a[++size]=a[rt];rt=size;
    		a[rt].sum+=v;
    		if(l==r)return;
    		int mid=l+r>>1;
    		if(k<=mid)insert(k,v,l,mid,a[rt].l);
    		else insert(k,v,mid+1,r,a[rt].r);
    	}
    	int query(int l,int r,int lside,int rside,int i,int j){
    		if(a[i].sum==a[j].sum)return 0;
    		if(l<=lside&&rside<=r)return (a[j].sum-a[i].sum);
    		int mid=lside+rside>>1,ans=0;
    		if(l<=mid)ans+=query(l,r,lside,mid,a[i].l,a[j].l);
    		if(mid<r)ans+=query(l,r,mid+1,rside,a[i].r,a[j].r);
    		return ans;
    	}
    }
    void radixsort(){
    	for(int i=0;i<=top;i++)tax[i]=0;
    	for(int i=1;i<=n;i++)tax[rk[i]]++;
    	for(int i=1;i<=top;i++)tax[i]+=tax[i-1];
    	for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i];
    }
    void suffixsort(){
    	top=30;
    	for(int i=1;i<=n;i++){
    		rk[i]=str[i]-'a'+1;
    		tp[i]=i;
    	}
    	radixsort();
    	for(int w=1,p=0;p<n;top=p,w<<=1){
    		p=0;
    		for(int i=1;i<=w;i++)tp[++p]=n-w+i;
    		for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;
    		radixsort();
    		swap(tp,rk);
    		rk[sa[1]]=p=1;
    		for(int i=2;i<=n;i++)
    		rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
    	}
    }
    void getheight(){
    	for(int i=1,j,k=0;i<=n;i++){
    		if(k)k--;
    		j=sa[rk[i]-1];
    		while(str[i+k]==str[j+k])k++;
    		height[rk[i]]=k;
    	}
    }
    void step(){
    	Log[0]=0;
    	for(int i=1;i<=n;i++){
    		f[i][0]=height[i];
    		Log[i]=log2(i);
    	}
    	for(int i=1;i<=Log[n];i++)
    	for(int j=1;j+(1<<(i-1))<=n;j++)
    	f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]);
    }
    inline int query(int l,int r){
    	l++;r++;
    	int k=Log[r-l];
    	return min(f[l][k],f[r-(1<<k)][k]);
    }
    bool check(int x,int l1,int r1,int l2,int r2){
    	int Left,Right;
    	int l=1,r=rk[l2];
    	while(l<r){
    		int mid=l+r>>1;
    		if(query(mid,rk[l2])<x)l=mid+1;
    		else r=mid;
    	}
    	Left=r;
    	l=rk[l2];r=n;
    	while(l<r){
    		int mid=l+r+1>>1;
    		if(query(rk[l2],mid)<x)r=mid-1;
    		else l=mid;
    	}
    	Right=r;
    	if(CT::query(l1,r1-x+1,1,n,root[Left-1],root[Right]))return true;
    	return false;
    }
    inline int solve(int l1,int r1,int l2,int r2){
    	int l=0,r=min(r1-l1+1,r2-l2+1);
    	while(l<r){
    		int mid=l+r+1>>1;
    		if(check(mid,l1,r1,l2,r2))l=mid;
    		else r=mid-1;
    	}
    	return r;
    }
    void work(){
    	int l1,l2,r1,r2;
    	while(m--){
    		l1=read();r1=read();l2=read();r2=read();
    		printf("%d
    ",solve(l1,r1,l2,r2));
    	}
    }
    void init(){
    	n=read();m=read();
    	scanf("%s",str+1);
    	suffixsort();
    	getheight();
    	step();
    	CT::buildtree();
    	for(int i=1;i<=n;i++){
    		root[i]=root[i-1];
    		CT::insert(sa[i],1,1,n,root[i]);
    	}
    }
    int main(){
    	init();
    	work();
        return 0;
    }
    

    据说这题可以用$SAM$搞事?

    暂且留个坑吧,等学完$SAM$再来填坑。。。

  • 相关阅读:
    在 AutoLayout 和 Masonry 中使用动画
    在 AutoLayout 和 Masonry 中使用动画
    Linux shell基础(五)sed命令
    Linux shell基础(五)sed命令
    Linux shell基础(五)sed命令
    Linux shell基础(五)sed命令
    直击高考人机大战:技术、争议与人族胜利
    直击高考人机大战:技术、争议与人族胜利
    直击高考人机大战:技术、争议与人族胜利
    JAVA面试精选【Java基础第一部分】
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9540875.html
Copyright © 2020-2023  润新知