#include<iostream>
#include<string.h>
#include<utility>
#include<algorithm>
using namespace std;
struct xx
{
string x;
short int d;
int lcp;
};
bool compare(const xx a,const xx b)
{
return a.x<b.x;
}
int findlcp(string a,string b)
{
int i=0,j=0,k=0;
while(i<a.length() && j<b.length())
{
if(a[i]==b[j])
{
k++;
i++;
j++;
}
else
{
break;
}
}
return k;
}
int main()
{
string a="banana";
xx b[100];
a=a+'$';
int len=a.length();
for(int i=0;i<len;i++)
{
b[i].x=a.substr(i);
b[i].d=i+1;
}
sort(b,b+len,compare);
for(int i=0;i<len;i++)
cout<<b[i].x<<" "<<b[i].d<<endl;
b[0].lcp=0;
b[1].lcp=0;
for(int i=2;i<len;i++)
{
b[i].lcp=findlcp(b[i].x,b[i-1].x);
}
for(int i=0;i<len;i++)
cout<<b[i].d<<" "<<b[i].lcp<<endl;
}
这是一个实现 Suffix Array。维基百科文章构造中我的问题是在最坏的情况下给出o(n)
所以在我的建设中:
所以对于第一个,即排序
如果我使用计数排序,我可能会减少到O(n)。如果我使用Count排序是否正确?我的理解是否正确?如果我的理解是错误的,请告诉我
有没有办法在O(n)时间内找到LCP?
答案 0 :(得分:4)
首先,关于你的两个陈述:
1)我正在使用stl sort对字符串的所有后缀进行排序。在最坏的情况下,这可能至少是O(nlogn)。所以我在这里违反O(n)结构。
std::sort
的复杂性比O(n log n)差。原因是O(n log n)假设存在O(n log n)个体比较,并且每个比较在O(1)时间中执行。后一个假设是错误的,因为你要排序字符串,而不是原子项(如字符或整数)。
由于作为主字符串的子串的字符串项的长度是O(n),因此可以肯定地说排序算法的最坏情况复杂度是O(n 2 log n)。
2)第二个是构建一个最长的公共前缀数组,给出O(n)。但我认为我的实现在O(n ^ 2)
是的,你的LCP数组构造是O(n 2 ),因为你正在运行lcp
函数n == len
次,而你的{{1函数需要一对字符串x,y的O(min(len(x),len(y)))时间。
接下来,关于您的问题:
如果我使用计数排序,我可以减少到O(n)。如果我使用Count排序是否正确?我的理解是否正确?如果我的理解是错误的,请告诉我。
不幸的是,您的理解不正确。如果您可以在O(1)时间内访问要排序的每个项目的原子键,则计数排序只是线性的。同样,这些项目的长度为字符串O(n)个字符,因此不起作用。
有没有办法在O(n)时间内找到LCP?
是。最近的后缀数组计算算法,包括DC算法(又名Skew算法),提供了计算LCP数组和后缀数组的方法,并在O(n)时间内完成。
DC算法的参考文献是JuhaKärkkäinen,Peter Sanders:简单的线性后缀阵列构造,自动机,语言和编程 计算机科学讲义2729,2003,pp 943-955(DOI 10.1007 / 3-540-45061-0_73)。 (但这不是唯一允许您在线性时间内执行此操作的算法。)
您可能还想看看此SO帖子中提到的开源实现:What's the current state-of-the-art suffix array construction algorithm?。除了后缀数组结构之外,其中使用的许多算法都能实现线性时间LCP数组构造(但并非所有实现都可能包含其实现;我不确定)。
如果您对Java中的示例没有问题,您可能还需要查看jSuffixArrays的代码。除其他算法外,它还包括DC算法的实现以及线性时间内的LCP阵列构造。
答案 1 :(得分:0)
jogojapan已全面回答了您的问题。仅提及优化的cpp实现,您可能要看一下here。
在GitHub出现故障的情况下在此处发布代码。
const int N = 1000 * 100 + 5; //max string length
namespace Suffix{
int sa[N], rank[N], lcp[N], gap, S;
bool cmp(int x, int y) {
if(rank[x] != rank[y])
return rank[x] < rank[y];
x += gap, y += gap;
return (x < S && y < S)? rank[x] < rank[y]: x > y;
}
void Sa_build(const string &s) {
S = s.size();
int tmp[N] = {0};
for(int i = 0;i < S;++i)
rank[i] = s[i],
sa[i] = i;
for(gap = 1;;gap <<= 1) {
sort(sa, sa + S, cmp);
for(int i = 1;i < S;++i)
tmp[i] = tmp[i - 1] + cmp(sa[i - 1], sa[i]);
for(int i = 0;i < S;++i)
rank[sa[i]] = tmp[i];
if(tmp[S - 1] == S - 1)
break;
}
}
void Lcp_build() {
for(int i = 0, k = 0;i < S;++i, --k)
if(rank[i] != S - 1) {
k = max(k, 0);
while(s[i + k] == s[sa[rank[i] + 1] + k])
++k;
lcp[rank[i]] = k;
}
else
k = 0;
}
};