• 【BZOJ2251】[BJWC2010] 外星联络(重拾后缀数组)


    点此看题面

    大致题意: 给定一个(01)串,依字典序输出所有出现次数大于(1)的子串的出现次数。

    前言

    既然只有0和1,Trie它不香吗。。。

    作为一道用来重拾后缀数组的题目,这道题其实真的挺水的。

    感觉最近水平还是有一定长进的,曾经无论如何都无法理解的后缀数组,今天来机房前在班里手玩模拟了一遍后缀排序(又是传说中的自习课加成?),结果突然就大彻大悟了。

    然后去看以前的博客,还揪出来三个地方写错了,看来无论是过去还是现在我都太菜了。。。

    大致做法

    假设你已经知道后缀排序和(Height)数组了。(不知道可以看这两篇博客:后缀数组入门(一)——后缀排序后缀数组入门(二)——Height数组与LCP

    首先我们考虑如何不重复地枚举子串。

    显然有一个基本的结论:子串是后缀的前缀。

    而对于后缀(_{SA_i}),根据(Height_i)的定义及性质,它的前(Height_i)个前缀都在后缀(_{SA_{i-1}})中出现过,而第(Height_{i}+1sim n-SA_i+1)个后缀都未曾出现过,于是我们只要枚举这些子串即可。

    然后考虑对于一个后缀(_{SA_i})的一个长度为(j)前缀,假设它出现(t)次,则需要满足:

    [min_{x=i+1}^{i+t-1}Height_xge j ]

    至于怎么对于每个(j)求出最大的(t),看似要二分,使复杂度平添一个(log),实则不然。

    如果我们倒序枚举(j),就会发现(t)是递增的,因此直接暴力搞就可以了。

    不过倒序枚举就需要倒序输出,可以开个栈来存储答案。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 3000
    using namespace std;
    int n,St[N+5];char s[N+5];
    class SuffixArray//后缀数组
    {
    	private:
    		int rk[N+5],p[N+5],t[N+5];
    		I void Sort(CI S)
    		{
    			RI i;for(i=0;i<=S;++i) t[i]=0;for(i=1;i<=n;++i) ++t[rk[i]];
    			for(i=1;i<=S;++i) t[i]+=t[i-1];for(i=n;i;--i) SA[t[rk[p[i]]]--]=p[i];
    		}
    		I void GetSA(char *s)//后缀排序
    		{
    			RI i;for(i=1;i<=n;++i) rk[p[i]=i]=s[i];
    			RI k,t=0,S='z';for(Sort(S),k=1;t^n;S=t,k<<=1)
    			{
    				for(t=0,i=1;i<=k;++i) p[++t]=n-k+i;
    				for(i=1;i<=n;++i) SA[i]>k&&(p[++t]=SA[i]-k);
    				for(Sort(S),i=1;i<=n;++i) p[i]=rk[i];
    				for(rk[SA[1]]=t=1,i=2;i<=n;++i)
    					rk[SA[i]]=(p[SA[i-1]]^p[SA[i]]||p[SA[i-1]+k]^p[SA[i]+k])?++t:t;
    			}
    		}
    		I void GetHeight(char *s)//求出Height数组
    		{
    			RI i,j,k=0;for(i=1;i<=n;++i) p[SA[i]]=i;
    			for(i=1;i<=n;++i)
    			{
    				if(k&&--k,p[i]==1) continue;j=SA[p[i]-1];
    				W(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;H[p[i]]=k;
    			}
    		}
    	public:
    		int SA[N+5],H[N+5];I void Init(char *s) {GetSA(s),GetHeight(s);}
    }S;
    int main()
    {
    	RI i,j,k,T=0;for(scanf("%d%s",&n,s+1),S.Init(s),i=1;i<=n;++i)//枚举后缀
    	{
    		for(j=n-S.SA[i]+1,k=i;j>S.H[i];--j) {W(k<=n&&S.H[k+1]>=j) ++k;St[++T]=k-i+1;}//倒序枚举前缀,维护k,开栈存储答案
    		W(T) St[T]^1&&printf("%d
    ",St[T]),--T;//输出栈中的答案
    	}return 0;
    }
    
  • 相关阅读:
    Java 第一章 初识Java
    Tomcat基础教程(三)
    Tomcat基础教程(二)
    Web Service相关工具的配置
    分布式版本控制系统Git的安装与使用
    个人项目小学四则运算 “软件”之初版
    结对项目四则运算 “软件”之升级版
    第一篇作业准备
    Linux常用命令入门文件、网络、系统及其他操作命令
    MySql5.7默认生成的密码无法正常登陆
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2251.html
Copyright © 2020-2023  润新知