字符串后缀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;
}