zoukankan      html  css  js  c++  java
  • 【BZOJ3230】相似子串 后缀数组+二分+RMQ

    【BZOJ3230】相似子串

    Description

    Input

    输入第1行,包含3个整数N,Q。Q代表询问组数。
    第2行是字符串S。
    接下来Q行,每行两个整数i和j。(1≤i≤j)。

    Output

    输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。

    Sample Input

    5 3
    ababa
    3 5
    5 9
    8 10

    Sample Output

    18
    16
    -1

    HINT

    样例解释
    第1组询问:两个子串是“aba”,“ababa”。f = 32 + 32 = 18。
    第2组询问:两个子串是“ababa”,“baba”。f = 02 + 42 = 16。
    第3组询问:不存在第10个子串。输出-1。

    数据范围
    N≤100000,Q≤100000,字符串只由小写字母'a'~'z'组成

    题解:一开始由于用SA还是SAM,然后看到下面的SOURCE一下子就不用犹豫了。。。这个提示也太明显了~

    如何字典序第k小的子串呢?考虑二分,问题就变成了问一个子串的字典序,这个可以直接在height数组上求出,答案就是n-sa[i]-height[i]的前缀和。

    然后相似程度就好求了,直接维护正串和反串的SA,用RMQ求出LCP即可。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn=100010;
    int n,m,Q;
    char str[maxn];
    int Log[maxn];
    ll s[maxn];
    inline ll rd()
    {
    	ll ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    struct SA
    {
    	int ra[maxn],rb[maxn],r[maxn],st[maxn],sa[maxn],rank[maxn],h[maxn],f[18][maxn];
    	void build()
    	{
    		int *x=ra,*y=rb,i,j,k,p;
    		for(i=0;i<n;i++)	st[x[i]=r[i]]++;
    		for(i=1;i<m;i++)	st[i]+=st[i-1];
    		for(i=n-1;i>=0;i--)	sa[--st[x[i]]]=i;
    		for(j=p=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)	y[p++]=sa[i]-j;
    			for(i=0;i<m;i++)	st[i]=0;
    			for(i=0;i<n;i++)	st[x[y[i]]]++;
    			for(i=1;i<m;i++)	st[i]+=st[i-1];
    			for(i=n-1;i>=0;i--)	sa[--st[x[y[i]]]]=y[i];
    			for(swap(x,y),i=p=1,x[sa[0]]=0;i<n;i++)
    				x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?p-1:p++;
    		}
    		for(i=1;i<n;i++)	rank[sa[i]]=i;
    		for(i=k=0;i<n-1;h[rank[i++]]=k)
    			for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    		for(i=1;i<n;i++)	f[0][i]=h[i];
    		for(j=1;(1<<j)<n;j++)	for(i=1;i+(1<<j)-1<n;i++)	f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
    	}
    	inline int query(int a,int b)
    	{
    		if(a==b)	return n-a;
    		a=rank[a],b=rank[b];
    		if(a>b)	swap(a,b);
    		a++;
    		int k=Log[b-a+1];
    		return min(f[k][a],f[k][b-(1<<k)+1]);
    	}
    }s1,s2;
    int find(ll x)
    {
    	int l=1,r=n+1,mid;
    	while(l<r)
    	{
    		mid=(l+r)>>1;
    		if(s[mid]>=x)	r=mid;
    		else	l=mid+1;
    	}
    	return r;
    }
    int main()
    {
    	n=rd(),Q=rd();
    	scanf("%s",str);
    	int i,c,d,la,lb;
    	ll a,b;
    	for(i=0;i<n;i++)	s1.r[i]=s2.r[n-i-1]=str[i]-'a'+1;
    	n++,m=27,s1.build(),m=27,s2.build(),n--;
    	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
    	for(i=1;i<=n;i++)	s[i]=s[i-1]+n-s1.sa[i]-s1.h[i];
    	for(i=1;i<=Q;i++)
    	{
    		a=rd(),b=rd(),c=find(a),la=a-s[c-1]+s1.h[c],d=find(b),lb=b-s[d-1]+s1.h[d];
    		if(c==n+1||d==n+1)	printf("-1
    ");
    		else
    		{
    			a=min(min(la,lb),s1.query(s1.sa[c],s1.sa[d])),b=min(min(la,lb),s2.query(n-s1.sa[c]-la,n-s1.sa[d]-lb));
    			printf("%lld
    ",a*a+b*b);
    		}
    	}
    	return 0;
    }//5 3 ababa 3 5 5 9 8 10 
  • 相关阅读:
    3D打印技术大潮
    有用网址
    linux下scp命令详解
    使用 GDB 调试多进程程序
    linux下top命令参数解释
    Sql动态查询拼接字符串的优化
    vmstat参数详解
    freebsd破解密码
    freebsd防火墙
    freebsd无法输入汉字
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7536497.html
Copyright © 2011-2022 走看看