字符串后缀Suffix 指的是从字符串的某个位置开始到其末尾的字符串字串
后缀数组 Suffix Array 指的是将某个字符串的所有后缀按字典序排序之后得到的数组,不过数组中不直接保存所有
的后缀子串,只要记录相应的位置就好了。
下面的代码使用倍增法来构造后缀数组,该算法的复杂度是 O(n log n)常数因子比较大。
基于后缀数组的字符串匹配,我们可以通过二分搜索来完成,算法复杂度是 O(|T|log|S|) 其中 S 是主串,T是模式串
具体的后缀数组的原理可以详细去看看国家队集训的论文,罗穗骞《后缀数组——处理字符串的有力工具》:
#include <iostream> #include <cstring> #include <cstddef> #include <cstdio> #include <string> #include <algorithm> using namespace std; const int MAXN = 10001; int n,k; int rank[MAXN+1],tmp[MAXN+1]; bool comp_sa(int i, int j) { if(rank[i] != rank[j]) return rank[i] < rank[j]; int ri = i+k <= n? rank[i+k] : -1; int rj = j+k <= n? rank[j+k] : -1; return ri < rj; } void calc_sa(string &S, int *sa) //计算字符串S的后缀数组 { n = S.size(); //初始长度为1 for(int i = 0; i <= n; i++) { sa[i] = i; rank[i] = i < n ? S[i] : -1; } for( k =1; k <= n; k *= 2) { sort(sa,sa+n+1,comp_sa); //先在tmp中临时存储新计算的rank,再转存回rank中 tmp[sa[0]] = 0; for(int i = 1; i <= n; i++) { tmp[sa[i]] = tmp[sa[i-1]] + (comp_sa(sa[i-1],sa[i]) ? 1: 0); } for(int i = 0; i <= n; i++) { rank[i] = tmp[i]; } } } void SuffixArrayMatch(string &S, int *sa, string T) { calc_sa(S,sa); int lhs = 0, rhs = S.size(); //使用二分查找 while(rhs - lhs > 1) { int mid = (lhs + rhs)>>1; int res = S.compare(sa[mid],T.size(),T); if(res < 0) lhs = mid; else if(res == 0) { cout<<"match at: "<<sa[mid]<<endl; lhs = mid; } else rhs = mid; } } int main() { string S = "abracadabra"; string T = "ab"; int *sa = new int[S.size()+1]; SuffixArrayMatch(S,sa,T); delete [] sa; sa = NULL; }