倍增法求后缀数组

本文深入解析了SA算法在字符串处理中的应用,通过实例“aabbabb”展示了如何利用倍增法求解排名数组(rank)、后缀数组(sa)及最长公共前缀数组(h)。并提供了详细的Pascal代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

SA是一种很好处理字符串的工具。
举个栗子很快可以明白
假设我们要处理“aabbabb”这个字符串,最终将得到:

字符串aabbabb
rank1375264
sa1527463

一些关键数组:

- sa[i]:排名第i名的后缀子串在什么位置。
- rank[i]:第i位后缀子串排名第几。
-h[i]:排名第i位的后缀子串与排名第i-1位的后缀子串最长公共前缀。

因此,我们所要做的主要工作是求以上三个数组,而本文所介绍的即是倍增法。


何为倍增?

由于笔者比较懒,所以不会太详细说,最直观的理解方式就是看代码。
简而言之,就是利用字符串长度为2k12k−1的信息(rank等)转移长度为2k2k的信息。

其他数组

-tp[i]:相当于排名第i位的“十位”在哪里(若当前求长度为2k2k,则储存当前位置前去2k12k−1,是一个辅助数组)。
-tax[i]:桶排所用的“桶”。

Pascal代码:

    const   maxn=1000005;
    var     s:ansistring;
            k,st:int64;
            n,max:longint;
            sa,rank,tp,h,tax:array[0..maxn] of longint;
    procedure sort;//桶排
    var     i:longint;
    begin
        fillchar(tax,sizeof(tax),0);
        for i:=1 to n do inc(tax[rank[tp[i]]]);
        for i:=1 to max do inc(tax[i],tax[i-1]);
        for i:=n downto 1 do begin            //为什么从
        //后往前呢?因为之前已经求好了“个位”的rank
                sa[tax[rank[tp[i]]]]:=tp[i];
                dec(tax[rank[tp[i]]]);
        end;
    end;
    function cmp(x,y,z:longint):boolean;
    begin
        exit((tp[x]=tp[y]) and (tp[x+z]=tp[y+z]));
    endprocedure G_Y;
    var     k,i,j:longint;
    begin
        k:=0;
        for i:=1 to n do begin
                if k>0 then dec(k);
                j:=sa[rank[i]-1];
                if j=0 then continue;
                while (i+k<=n) and (j+k<=n) and (s[i+k]=s[j+k]) do inc(k);
                h[rank[i]]:=k;
        end;
    end;
    procedure suffix;
    var     i,p,l:longint;
    begin
        sort;
        l:=1;
        while  l<n do begin
                for i:=1 to l do tp[i]:=n-i+1;
                p:=l;
                for i:=1 to n do if sa[i]>l then begin
                        inc(p);
                        tp[p]:=sa[i]-l;
                end;
                sort;
                tp:=rank;
                rank[sa[1]]:=1;
                p:=1;
                for i:=2 to n do if cmp(sa[i-1],sa[i],l) then
                rank[sa[i]]:=p else begin//避免rank的重复
                        inc(p);
                        rank[sa[i]]:=p;
                end;
                max:=p;
                l:=l*2;
        end;
    end;
    procedure init;
    var     i:longint;
    begin
        readln(s);
        readln(k);
        n:=length(s);
        for i:=1 to n do begin
                rank[i]:=ord(s[i]);
                if rank[i]>max then max:=rank[i];
                tp[i]:=i;//初始化,把“十位”当做“个位”
        end;
    end;
    begin
        init;//
        suffix;//求rank与sa的东西
        G_Y;//求h的东西
    end.

应用:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值