• [模板] 后缀数组


    概述

    后缀数组(Suffix Array), 是一种将字符串所有后缀排序的一种算法。
    通过排序后的后缀,我们可以得到字符串的许多性质,如重复出现的字串等。

    算法

    (n) 表示字符串长度, (Sigma) 表示字符集大小.

    常见的后缀数组算法有:

    1. 倍增算法
      • 时间复杂度 $ O(n log n) $
      • 常数较小
      • 代码较短
    2. DC3算法
      • 时间复杂度 $ O(n) $
      • 常数较大
      • 代码较长
    3. SA-IS算法
      • 时间复杂度 $ O(n) $
      • 常数中等
      • 代码较长
      • 窝三个月之前还会,现在就不会了
    4. 后缀自动机遍历
      • 时间复杂度 (O(n*Sigma)) , 空间复杂度 (O(n*Sigma))
      • 或者时间复杂度 (O(n log n)) , 空间复杂度 (O(n)) (map实现)
      • 常数中等
      • 代码较长
      • 但是并没有倍增好写

    倍增算法

    放两个链接:

    height

    (S_i) 表示 (S) 的第 (i) 个后缀.

    定义

    [height(i) = lcp(S_{sa(i)}, S_{sa(i-1)}) ]

    [height(rk(i)) ge height(rk(i-1)) - 1 ]

    代码

    #define rep(i,l,r) for(register int i=(l);i<=(r);++i)
    #define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
    
    const int ssz=1e6+5;
    char s[ssz];
    
    struct tsa{
    	int n,sigma,t1[ssz],t2[ssz],sa[ssz],c[ssz],*rk=t1,*tp=t2;
    	int hi[ssz];
    	int l2n[ssz],stt[21][ssz];
    	void pr(){
    		rep(i,1,n)printf("%d %d %d
    ",sa[i],rk[i],tp[i]);
    	}
    	//from http://www.cnblogs.com/zwfymqz/p/8413523.html
    	//sa[i]: 长度为w的后缀中, 排名为i的后缀的位置
    	//rk[i]: 长度为w的后缀中, 从第i个位置开始的后缀的排名
    	//tp[i]: 长度为2w的后缀中, 第二关键字排名为i的后缀的位置
    	void rsort(){//sort i([1,n]) by (rk[i],inv_tp[i])
    		rep(i,0,sigma)c[i]=0;
    		rep(i,1,n)++c[rk[i]];
    		rep(i,1,sigma)c[i]+=c[i-1];
    		repdo(i,n,1)sa[c[rk[tp[i]]]--]=tp[i];
    	}
    	void suffixsort(char *s){
    		rep(i,1,n)rk[i]=s[i],tp[i]=i;
    		rsort();
    		for(int w=1,p=0;p<n;sigma=p,w<<=1){
    			p=0;
    			rep(i,1,w)tp[++p]=n-w+i; //不存在后w个字符的串的第二关键字最小
    			rep(i,1,n)if(sa[i]>w)tp[++p]=sa[i]-w; //前w个字符不会作为第二关键字 
    			rsort();
    			//calc rk (|第一关键字|=2w)
    			swap(tp,rk);//del tp; tp=rk(|第一关键字|=w) 
    			p=1,rk[sa[1]]=p;
    			rep(i,2,n){
    				rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
    			}
    		}
    	}
    	
    	void gethi(char *s){
    		int k=0;
    		rep(i,1,n){
    			if(k>0)--k;
    			while(s[i+k]==s[sa[rk[i]-1]+k])++k;
    			hi[rk[i]]=k;
    		}
    	}
    	
    	void rmq(){
    		int l=0;
    		rep(i,1,n)stt[0][i]=hi[i],l2n[i]=(i==(1<<(l+1))?++l:l);
    		rep(i,1,l2n[n]){
    			repdo(j,n-(1<<i)+1,1){
    				stt[i][j]=min(stt[i-1][j],stt[i-1][j+(1<<(i-1))]);
    			}
    		}
    	}
    	
    	int lcp(int x,int y){//x&y are ranks
    		if(x==y)return n-sa[x]+1;
    		if(x>y)swap(x,y);
    		++x;
    		int l=l2n[y-x+1];
    		return min(stt[l][x],stt[l][y-(1<<l)+1]);
    	}
    	
    	int lcp1(int x,int y,int l1,int l2){
    		return min(lcp(x,y),min(l1,l2));
    	}
    	//O(n log n + sigma)
    	void init(char *s,int n0,int sig0){
    		sigma=sig0,n=n0;
    		suffixsort(s);
    		gethi(s);
    		rmq();
    	}
    }sa;
    
    //init e.g.
    	sig0=27;
    	rep(i,1,n)s[i]-='a'-1;
    	a.init(s,n,sig0);
    

    for copying

    const int ssz=1e6+5;
    char s[ssz];
    
    struct tsa{
    	int n,sigma,t1[ssz],t2[ssz],sa[ssz],c[ssz],*rk=t1,*tp=t2;
    	int hi[ssz];
    	int l2n[ssz],stt[21][ssz];
    	void pr(){
    		rep(i,1,n)printf("%d %d %d
    ",sa[i],rk[i],tp[i]);
    	}
    	void rsort(){
    		rep(i,0,sigma)c[i]=0;
    		rep(i,1,n)++c[rk[i]];
    		rep(i,1,sigma)c[i]+=c[i-1];
    		repdo(i,n,1)sa[c[rk[tp[i]]]--]=tp[i];
    	}
    	void suffixsort(char *s){
    		rep(i,1,n)rk[i]=s[i],tp[i]=i;
    		rsort();
    		for(int w=1,p=0;p<n;sigma=p,w<<=1){
    			p=0;
    			rep(i,1,w)tp[++p]=n-w+i;
    			rep(i,1,n)if(sa[i]>w)tp[++p]=sa[i]-w;
    			rsort();
    			swap(tp,rk);
    			p=1,rk[sa[1]]=p;
    			rep(i,2,n){
    				rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
    			}
    		}
    	}
    	void gethi(char *s){
    		int k=0;
    		rep(i,1,n){
    			if(k>0)--k;
    			while(s[i+k]==s[sa[rk[i]-1]+k])++k;
    			hi[rk[i]]=k;
    		}
    	}
    	void rmq(){
    		int l=0;
    		rep(i,1,n)stt[0][i]=hi[i],l2n[i]=(i==(1<<(l+1))?++l:l);
    		rep(i,1,l2n[n]){
    			repdo(j,n-(1<<i)+1,1){
    				stt[i][j]=min(stt[i-1][j],stt[i-1][j+(1<<(i-1))]);
    			}
    		}
    	}
    	
    	int lcp(int x,int y){//x&y are ranks
    		if(x==y)return n-sa[x]+1;
    		if(x>y)swap(x,y);
    		++x;
    		int l=l2n[y-x+1];
    		return min(stt[l][x],stt[l][y-(1<<l)+1]);
    	}
    
    	int lcp1(int x,int y,int l1,int l2){
    		return min(lcp(x,y),min(l1,l2));
    	}
    
    	void init(char *s,int n0,int sig0){
    		sigma=sig0,n=n0;
    		suffixsort(s);
    		gethi(s);
    		rmq();
    	}
    }sa;
    

    SA-IS

    说不定什么时候窝就回来填坑了

  • 相关阅读:
    Poj3295 tautology
    Poj2586 每五个月都是亏
    Poj 2109 k^n = p.
    Poj2109 (2) k^n = p.
    Poj2109 (1) k^n = p.
    Poj2965 冰箱的开关
    Poj1328 用雷达覆盖所有的岛屿
    FASTER-RCNN
    卷积、池化计算
    理论感受野的计算
  • 原文地址:https://www.cnblogs.com/ubospica/p/10162594.html
Copyright © 2020-2023  润新知