• 后缀数组


    对于概念去看白书或者论文吧(《后缀数组——处理字符串的有力工具》by:罗穗骞)

    这里放上模板,一些解释在注释里。

    #include <cstdio>
    #include <cstring>
    #define for1(i,a,n) for(i=a;i<=(n);++i)
    #define for2(i,a,n) for(i=a;i<(n);++i)
    #define for3(i,a,n) for(i=a;i>=(n);--i)
    #define for4(i,a,n) for(i=a;i>(n);--i)
    #define CC(i,a) memset(i, a, sizeof(i))
    #define min(a,b) ((a)<(b)?(a):(b))
    
    using namespace std;
    
    const int N=1000000;
    char s[N];
    int sa[N], t1[N], t2[N], c[N], r[N], height[N], rank[N];
    
    void hz(int* r, int n, int m) {
    	int i, j, p, *x=t1, *y=t2, *t;
    	for2(i, 0, m) c[i]=0;
    	for2(i, 0, n) c[ x[i]=r[i] ]++;	//给排名为x[i]的计数
    	for2(i, 1, m) c[i]+=c[i-1];		//将计数数组c累加,目的是给sa提供下标
    	for3(i, n-1, 0) sa[ --c[x[i]] ]=i;	//sa数组用c提供的名次下标更新原数组下标
    	for(j=1, p=0; j<=n	&& p<n; j<<=1, m=p) { //p<n时退出,因为此时排名已经不同,此优化很重要,并且每一次将m更新为最大值p
    		p=0;
    		for2(i, n-j, n) y[p++]=i;	//y数组充当第二关键字的sa数组,这里是初始化那些没有第二关键字的,即第二关键字为0的,所以他们最小放在前面
    		for2(i, 0, n) if(sa[i]>=j) y[p++]=sa[i]-j;	//因为是第二关键字,所以要标到第一关键字的下标来
    		for2(i, 0, m) c[i]=0;	//同之前的基数排序一样
    		for2(i, 0, n) c[ x[y[i]] ]++;	//用第二关键字的下标引导rank,找到对应第一关键字的rank累计
    		for2(i, 1, m) c[i]+=c[i-1];		//同上
    		for3(i, n-1, 0) sa[ --c[x[y[i]]] ]=y[i];	//sa数组用c提供的 第二关键字尽量大中的 第一关键字下标 的名次更新到这个这个 第二关键字尽量大中的 第一关键字下标
    		for(t=x, x=y, y=t, p=1, x[sa[0]]=0, i=1; i<n; ++i) //y数组可以不要了,将它赋值为这一次的rank,x数组作为下一个rank数组,所以更新
    			x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]?p-1:p++;	//当第一关键字的rank相等并且第二关键字的rank相等说明这个下标的rank和其中一个相等,所以名次还是为上一个
    	}
    }
    
    void getheight(int n) {
    	int i, j, k;
    	for1(i, 1, n) rank[sa[i]]=i;	//因为sa下标从1开始,rank下标从0开始,值域从1开始
    	for(k=i=0; i<n; height[rank[i++]]=k)	//根据h[i]>=h[i-1]-1,我们循环的是h[i],所以h[i-1]不需保存,其中h[i]我们用k来代替了
    		for(k?k--:0, j=sa[rank[i]-1]; s[i+k]==s[j+k]; k++);		//因为h[i-1]-1是.............(自己看论文或白书去)。我们只需要根据前一个后缀来更新最大前缀。
    }
    
    int main() {
    	scanf("%s", s);
    	int i, n=strlen(s);
    	for2(i, 0, n) r[i]=s[i]-'a'+1; //这里的r随便,如果知道范围的话,c数组可以相应的开小一些,我这里直接27了。。
    	hz(r, n+1, 27); //为了让sa的下标从1开始,我们插入了一个比所有字符还要小的字符到末尾,所以sa的下标就从1开始了,方便计算height而不会导致越界~
    	//...
    	getheight(n);
    	//...
    	return 0;
    }
    

    例题:

    1. 【BZOJ】1692 & 1640: [Usaco2007 Dec]队列变换(后缀数组+贪心)
    2. 【BZOJ】1031: [JSOI2007]字符加密Cipher(后缀数组)
  • 相关阅读:
    0015 Java学习笔记-集合-TreeMap集合
    0014 Java学习笔记-集合-HashMap集合
    0013 Java学习笔记-面向对象-static、静态变量、静态方法、静态块、单例类
    0012 win7x64安装CentOS7
    0011《SQL必知必会》笔记07 数据的插入、更新和删除
    0010《SQL必知必会》笔记06-表的修改与删除
    0009《SQL必知必会》笔记05-表的创建与约束
    0008《SQL必知必会》笔记04-子查询、联接与组合查询
    0007《SQL必知必会》笔记03-汇总与分组数据
    0006 《SQL必知必会》笔记02-计算字段与函数
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/3864075.html
Copyright © 2020-2023  润新知