[BZOJ1031]-[JSOI2007]字符加密Cipher-后缀数组模板(附自己的理解)

来源:互联网 发布:碑文 软件 编辑:程序博客网 时间:2024/06/11 01:35

说在前面

之前写后缀数组都是在背板子,而且已经很久没碰过这玩意了….忘得一干二净w
原计划今天下午开始回血,没料到AC自动机的好题实在是太多了,不得不去写一遍,回血计划被迫推迟到了晚上=A=
不得不说即使是现在,看后缀数组仍然是一个很有难度的东西啊


题目

BZOJ1031传送门
懒得粘题面了…
不是权限题,点进去就可以看


解法

(重点不在这里)
会后缀数组基本就是一眼题了= =
把这个字符串复制一遍接到它本身后面去,这样就相当于是一个环的样子。把后缀数组算出来,然后输出对应位置的字符即可。那些SA[i]比原长度大的直接忽略就好(因为它们的开头在第二圈)


下面是自带大常数的代码

#include <cstdio>#include <cstring>#include <algorithm>using namespace std ;int len , N ;int tp[200005] , tax[200005] , RA[200005] , SA[200005] ;char ss[200005] ;bool equ( int *f , int x , int y , int len ){    int xl = ( x + len > N ? 0 : f[x+len] ) ,        yl = ( y + len > N ? 0 : f[y+len] ) ;    return f[x] == f[y] && xl == yl ;}void Rsort( int m ){    for( int i = 1 ; i <= m ; i ++ ) tax[i] = 0 ;    for( int i = 1 ; i <= N ; i ++ ) tax[ RA[tp[i]] ] ++ ;    for( int i = 1 ; i <= m ; i ++ ) tax[i] += tax[i-1] ;    for( int i = N ; i ; i -- ) SA[ tax[RA[tp[i]]]-- ] = tp[i] ;}void getSA(){    for( int i = 1 ; i <= N ; i ++ )        RA[i] = ss[i] , tp[i] = i ;    int m = 127 ; Rsort( m ) ; //第一遍排序相当于预处理    //也就是处理出二元组中前半部分的名次,相同的位置靠前名次也靠前    //例如abcbac中,两个a的SA下标是1和2,而b是3和4,c是5和6    for( int w = 1 , p ; p != N ; w += w , m = p ){        p = 0 ;        for( int i = N - w + 1 ; i <= N ; i ++ ) tp[++p] = i ;        for( int i = 1 ; i <= N ; i ++ )//处理二元组排第几的是谁            if( SA[i] > w ) tp[++p] = SA[i] - w ;//当SA小于等于w时,无法作为二元组            //原来排第i的是SA[i],那么除了那些没有二元组的,现在二元组排第p的就是SA[i]-w.(SA[i]-w)是first的位置        Rsort( m ) ; memcpy( tp , RA , sizeof( RA ) ) ;        RA[SA[1]] = p = 1 ;        for( int i = 2 ; i <= N ; i ++ )            RA[ SA[i] ] = ( equ( tp , SA[i] , SA[i-1] , w ) ? p : ++ p ) ;    }}void solve(){    getSA() ;    for( int i = 1 ; i <= N ; i ++ )        if( SA[i] <= len ) printf( "%c" , ss[SA[i]+len-1] ) ;}int main(){    scanf( "%s" , ss + 1 ) ;    len = strlen( ss + 1 ) ; N = ( len << 1 ) ;    for( int i = len + 1 ; i <= N ; i ++ )        ss[i] = ss[i-len] ;    solve() ;}

一点理解

其实个人觉得那个「五分钟学会XXX」的博客讲的已经很清楚了
SA[i]表示的是后缀排名第i的是以SA[i]开始的后缀
RA[i]表示以i开始的后缀排名为RA[i]

在求后缀数组之前需要预处理二元组中的first的排名,也就是代码里for循环外面的那个Rsort
里面用于Rsort的tp数组就是在记录二元组的second的信息。Rsort函数里累加桶的时候还是根据first来累加的,然后用second来判断准确的SA是什么。

注意RA数组里的数字代表的是当前first的种类,因此RA数组只有在后缀数组计算完毕时,才包含了1到len的数字。而SA数组一开始就包含了1到len的所有数字,仅仅是因为不可能SA[i]又等于j又等于k(毕竟在排序完成之前,j和k的后缀排名可能都为i),所以为了方便才让SA[i]=j而SA[i+1]=k。
但是这里就要注意,如果一开始规定相同first靠后的编号大,那么就要一直保持这个性质,而不能一会靠前编号大一会靠后编号大,这样在形如「abababaaaaaaaaaaaa」这样的数据下就会GG,这也就是「 for( int i = N - w + 1 ; i <= N ; i ++ ) tp[++p] = i ; 」的for顺序不能倒过来写的原因

阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 眼睛被东西砸了怎么办 眼睛被打了一下怎么办 眼睛被树枝伤了怎么办 眼球被打了一下怎么办 眼睛黄有血丝是怎么办 上眼皮有异物感怎么办 20岁眼周围皱纹怎么办 才22岁眼睛皱纹怎么办 眼周围发干皱纹怎么办 眼周围淤血肿了怎么办 每到秋季就咳嗽怎么办 左肾泥沙样结石怎么办 双肾泥沙样结石怎么办 温州市民卡丢了怎么办 上眼皮过敏肿了怎么办 上眼皮又痒又肿怎么办 上眼皮红肿痛是怎么办 上眼皮肿的厉害怎么办 眼皮肿了还痒痒怎么办 眼睛被手指戳到怎么办 打球眼睛撞肿了怎么办 打球时眼睛被戳怎么办 狗眼睛被打充血怎么办 一只眼睛磨的慌怎么办 5个月宝宝结膜炎怎么办 金毛眼屎多白色怎么办 金毛眼红有眼屎怎么办 狗狗眼睛上火了怎么办 狗上火了眼屎多怎么办 金毛走路扭腰怎么办 金毛流鼻涕微黄怎么办 狗狗下眼皮红了怎么办 金毛眼睛打肿了怎么办 金毛的眼睛红怎么办 眼睛干涩有红血丝怎么办 小孩子眼睛红有眼屎怎么办 狗狗的肉垫粗糙怎么办 狗狗眼睛变蓝色怎么办 脸被太阳晒伤了怎么办 皮肤晒伤红肿痒怎么办 3岁儿童频繁眨眼怎么办