这是一个相当简单的 C++ 实现,尽管 build() 过程在 O(N lg^2 N) 时间构建后缀数组。 lcp_compute() 过程具有线性复杂性。我在很多编程比赛中都使用过这段代码,它从来没有让我失望过 :)
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAX = 200005;
char str[MAX];
int N, h, sa[MAX], pos[MAX], tmp[MAX], lcp[MAX];
bool compare(int i, int j) {
if(pos[i] != pos[j]) return pos[i] < pos[j]; // compare by the first h chars
i += h, j += h; // if prefvious comparing failed, use 2*h chars
return (i < N && j < N) ? pos[i] < pos[j] : i > j; // return results
}
void build() {
N = strlen(str);
for(int i=0; i<N; ++i) sa[i] = i, pos[i] = str[i]; // initialize variables
for(h=1;;h<<=1) {
sort(sa, sa+N, compare); // sort suffixes
for(int i=0; i<N-1; ++i) tmp[i+1] = tmp[i] + compare(sa[i], sa[i+1]); // bucket suffixes
for(int i=0; i<N; ++i) pos[sa[i]] = tmp[i]; // update pos (reverse mapping of suffix array)
if(tmp[N-1] == N-1) break; // check if done
}
}
void lcp_compute() {
for(int i=0, k=0; i<N; ++i)
if(pos[i] != N-1) {
for(int j=sa[pos[i]+1]; str[i+k] == str[j+k];) k++;
lcp[pos[i]] = k;
if(k) k--;
}
}
int main() {
scanf("%s", str);
build();
for(int i=0; i<N; ++i) printf("%d\n", sa[i]);
return 0;
}
注意:如果你想让build()过程的复杂度变成O(N lg N),你可以用基数排序代替STL排序,但这会使代码复杂化。 p>
编辑:对不起,我误解了你的问题。虽然我没有用后缀数组实现字符串匹配,但我想我可以为你描述一个简单的非标准但相当有效的字符串匹配算法。您将获得两个字符串,text 和 pattern。给定这些字符串,您将创建一个新字符串,我们称之为concat,它是两个给定字符串的连接(首先是text,然后是pattern)。您在concat 上运行后缀数组构造算法,然后生成普通的 lcp 数组。然后,在刚刚构建的后缀数组中搜索长度为pattern.size() 的后缀。让我们称它在后缀数组pos 中的位置。然后你需要两个指针lo 和hi。在开始lo = hi = pos。你减少lo 而lcp(lo, pos) = pattern.size() 并且你增加hi 而lcp(hi, pos) = pattern.size()。然后在[lo, hi] 范围内搜索长度至少为2*pattern.size() 的后缀。如果你找到它,你就找到了匹配。否则,不存在匹配项。
编辑[2]:我一有实现就会回来...
编辑[3]:
这里是:
// It works assuming you have builded the concatenated string and
// computed the suffix and the lcp arrays
// text.length() ---> tlen
// pattern.length() ---> plen
// concatenated string: str
bool match(int tlen, int plen) {
int total = tlen + plen;
int pos = -1;
for(int i=0; i<total; ++i)
if(total-sa[i] == plen)
{ pos = i; break; }
if(pos == -1) return false;
int lo, hi;
lo = hi = pos;
while(lo-1 >= 0 && lcp[lo-1] >= plen) lo--;
while(hi+1 < N && lcp[hi] >= plen) hi++;
for(int i=lo; i<=hi; ++i)
if(total-sa[i] >= 2*plen)
return true;
return false;
}