后缀数组的实现
什么是后缀数组
后缀数组是处理字符串的有力工具。后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也并不逊色,而且它比后缀树所占用的内存空间小很多。1
上边参考的文章中,后缀数组原理讲解的很清楚,但是代码实现简直就是一锅粥,不知所云。因此我自己实现了一下,原理的话就请参考这篇文章了。
后缀数组的生成
在实现阶段,我没有过多的考虑算法的性能,也没有参考该文章所说的2倍增算法,主要使用了c++容器的一些特性。
实现步骤如下:
- 生成后缀排序数组
- 生成后缀数组
- 生成高度数组(排名相邻的两个后缀的最长公共前缀)
代码片段如下:
#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;
//字典序比较两个字符串,iter_1 > iter_2 ->return true
bool Cmp(string::iterator iter_1, string::iterator iter_2,string::iterator end)
{
string tmp_str_1(iter_1, end);
string tmp_str_2(iter_2, end);
//tmp_str_1 > tmp_str_2
if (tmp_str_1.compare(tmp_str_2) > 0)
{
return true;
}
else
{
return false;
}
}
//求出来某个后缀数组suf的排名
int Rank(string str, unsigned suf)
{
int rank = 0;
for (auto iter = str.begin(); iter != str.end(); iter++)
{
if (iter == (str.begin() + suf))
{
continue;
}
else if ( Cmp(str.begin() + suf, iter, str.end()) )
{
rank++;
}
}
return rank;
}
//创建一个排名数组
void set_suffix_rank_array(string str, int suff_rank[],unsigned len)
{
for(unsigned i = 0; i < len; i++)
{
suff_rank[i] = Rank(str, i);
}
}
//创建后缀数组
void set_suffix_array(int suff_rank[], int suff_array[], unsigned len)
{
for (unsigned i = 0; i < len; i++)
{
suff_array[suff_rank[i]] = i;
}
}
//依次将排名相近的两个后缀数组从前向后计数(有相同前缀)
void set_height_array(string str,int suff_rank[], int suff_array[],int height[], unsigned len)
{
unsigned h(0),r(0);
for (unsigned i = 0; i < len; i++)
{
h = 0;
r = suff_rank[i];//只是为了表达方便
//其中的i表示的是suff_array[suff_rank[i]],为了书写方便
while ((r+ 1<len) && (i + h < len) && (suff_array[r + 1] + h < len )
&& (str[i + h] == str[suff_array[r + 1] + h]))
{
h++;
}
height[suff_rank[i]] = h;
}
}
/*主要参考“后缀数组-处理字符串的有力工具”,论文作者是“罗穗骞”
主要要解决的问题:
1、最长重复出现子串;
2、最长公共子串;
*/
#include"MakeSuffixArray.h"
#include<Windows.h>
#include<string>
int main(void)
{
string str("aabca");
string str1("aab");
str = str + '0' + str1;
cout << str << endl;
auto len = str.size();
int *suff_rank = new int[len]();
int *suff_array = new int[len]();
int *height = new int[len]();
//创建一个排名数组suff[str.size()]
set_suffix_rank_array(str, suff_rank, len);
//创建后缀数组,排第几的是谁
set_suffix_array(suff_rank, suff_array, len);
//创建高度数组
set_height_array(str, suff_rank, suff_array, height, len);
/*求一个字符串中最长重复子串,并输出。
解题思路:只需求得height中的最大值,并根据height数组中的位置反推出字符串中的位置
*/
int pos(0), lengthest = height[0];
for (unsigned i = 1; i < len-1; i++)
{
if (height[i] > lengthest)
{
lengthest = height[i];
pos = i;
}
}
cout <<suff_array[pos]<< " "<<lengthest << endl;
for (unsigned j = 0; j < lengthest; j++)
{
cout << str[suff_array[pos] + j];
}
cout << endl;
/*求最长公共子串的问题
解题思路:先将两个字符串前后相连,然后求最长子串,
要注意两个子串不在同一字符串中
*/
system("pause");
return 0;
}
更详细的代码请转: github
不知道是我不会用还是因为优快云的markdown编辑器本身就有问题呢,看着好丑LOL