基本上一搜后缀数组网上的模板都是《后缀数组——处理字符串的有力工具》这一篇的注释,O(nlogn)的复杂度确实很强大,但对于初次接触(比如窝)的人来说理解起来也着实有些困难(比如窝就活活好了两天的光阴。。),看了那么多材料感觉《挑战程序设计》的后缀数组解释理解起来会相对容易很多,然而它的复杂度是O(nlog2n)的,主要区别是对子串排序的时候前者用了计数排序--O(n),而后者用了快排--O(nlogn),这就导致了最终的复杂度后者比前者多了一个O(logn)

 

O(nlog2n)算法

先附清爽版求SA(Suffix_Array)模板,主要思想当然仍是倍增法;

 1 /*0(nlog(n)^2)*/
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cstddef>
 5 #include <cstdio>
 6 #include <string>
 7 #include <algorithm>
 8 using namespace std;
 9 const int MAXN = 10001;
10 int n,k;
11 int rank[MAXN+1],tmp[MAXN+1];
12 
13 bool comp_sa(int i, int j)
14 {
15     if(rank[i] != rank[j])
16         return rank[i] < rank[j];
17     int ri = i+k <= n? rank[i+k] : -1;
18     int rj = j+k <= n? rank[j+k] : -1;
19     return ri < rj;
20 }
21 
22 void calc_sa(string &S, int *sa) //计算字符串S的后缀数组
23 {
24     n = S.size();
25     //初始长度为1
26     for(int i = 0; i <= n; i++)
27     {
28         sa[i] = i;
29         rank[i] = i < n ? S[i] : -1;
30     }
31 
32     for( k = 1; k <= n; k *= 2)
33     {
34         sort(sa,sa+n+1,comp_sa); //双关键字快排
35 
36         //先在tmp中临时存储新计算的rank,再转存回rank中
37         tmp[sa[0]] = 0;
38         for(int i = 1; i <= n; i++)
39         {
40             tmp[sa[i]] = tmp[sa[i-1]] + (comp_sa(sa[i-1],sa[i]) ? 1: 0);
41         }
42         for(int i = 0; i <= n; i++)
43         {
44             rank[i] = tmp[i];
45         }
46     }
47 }
48 
49 int main()
50 {
51     string S = "abracadabra";
52     int *sa = new int[S.size()+1];
53      SuffixArrayMatch(S,sa,T);
54      delete [] sa;
55      sa = NULL;
56 }
View Code

相关文章: