#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5;
int sa[N],rank[N],rank2[N],height[N],cnt[N],*x,*y;
/*
* a radix_sort which is based on the y[].
* how ? ahhhh, the last reverse for is the solution.
* and the adjacant value of sa[] might have the same rank.
*/
void radix_sort(int n,int sz)
{
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;i++)
cnt[ x[ y[i] ] ]++;
for(int i=1;i<sz;i++)
cnt[i] += cnt[i-1];
for(int i=n-1;i>=0;i--)
sa[ --cnt[ x[ y[i] ] ] ] = y[i];
}
/*
* sa[i] represents the ith suffix string is which one.
* rank[i] represents the suffix string [i,n]'s rank.
* sz is the max_rank of text in that time.
* x[] represents the true pointer of rank[] in that time and it may be not unique.
* y[] is the location of text[] which is sorted by 2nd key in that time before swap(x,y).
*/
void get_sa(char text[],int n,int sz=128)
{
x = rank, y = rank2;
for(int i=0;i<n;i++)
x[i] = text[i], y[i] = i;
radix_sort(n,sz);
for(int len=1;len<n;len<<=1)
{
int yid = 0;
for(int i=n-len;i<n;i++)
y[yid++] = i;
for(int i=0;i<n;i++)
if(sa[i] >= len)
y[yid++] = sa[i] - len;
radix_sort(n,sz);
swap(x,y);
x[ sa[0] ] = yid = 0;
for(int i=1;i<n;i++)
{
if(y[ sa[i-1] ]==y[ sa[i] ] && sa[i-1]+len<n && sa[i]+len<n && y[ sa[i-1]+len ]==y[ sa[i]+len ])
x[ sa[i] ] = yid;
else
x[ sa[i] ] = ++yid;
}
sz = yid + 1;
if(sz >= n)
break;
}
for(int i=0;i<n;i++)
rank[i] = x[i];
}
/*
* height[] represents the longest common prefix of suffix [i-1,n] and [i,n].
* height[ rank[i] ] >= height[ rank[i-1] ] - 1.
..... let's call [k,n] is the suffix which rank[k] = rank[i-1] - 1,
...=> [k+1,n] is a suffix which rank[k+1] < rank[i]
..... and the lcp of [k+1,n] and [i,n] is height[ rank[i] ] - 1.
..... still unknow ? height[ rank[i] ] is the max lcp of rank[k] and rank[i] which rank[k] < rank[i].
*/
void get_height(char text[],int n)
{
int k = 0;
for(int i=0;i<n;i++)
{
if(rank[i] == 0)
continue;
k = max(0,k-1);
int j = sa[ rank[i]-1 ];
while(i+k<n && j+k<n && text[i+k]==text[j+k])
k++;
height[ rank[i] ] = k;
}
}
namespace RMQ
{
int dp[20][N];
void init(int c[],int n)
{
for(int i=0;i<n;i++)
dp[0][i] = c[i];
for(int j=1;j<20;j++)
for(int i=0;i+(1<<j)-1<n;i++)
dp[j][i] = min(dp[j-1][i],dp[j-1][i+(1<<(j-1))]);
}
int _log2(int n)
{
int ret = 0;
while(1<<(ret+1) <= n)
ret++;
return ret;
}
int get_min(int a,int b)
{
int k = _log2(b-a+1);
return min(dp[k][a],dp[k][b-(1<<k)+1]);
}
}
int lcp(int a,int b)
{
a = rank[a] , b = rank[b];
if(a > b) swap(a,b);
return RMQ::get_min(a+1,b);
}
分享到:
相关推荐
在实现后缀数组的倍增算法时,通常会用到辅助的数据结构,如最小堆或者LCP(Longest Common Prefix,最长公共前后缀)阵列。这些数据结构有助于加速比较和调整后缀的顺序。例如,最小堆可以快速找到当前阶段最小的...
根据给定的信息,本文将详细解释“后缀数组C++实现代码”中涉及的重要知识点,包括后缀数组、倍增算法、以及RMQ(Range Minimum Query)问题。 ### 一、后缀数组 #### 1.1 定义 后缀数组是一种数据结构,它存储了...
倍增算法利用了后缀数组的一些性质,通过逐步增加比较长度来构建,其时间复杂度可以达到O(n log^2 n),n为字符串长度。而DC3(Doubly-Comparing Three Characters)算法则是基于字符的字典序,通过三字符的比较来...
DA算法的基本思想是通过倍增的方法构造后缀数组,它利用了后缀数组的递推性质,能够通过递归的方式加速后缀的比较过程,从而达到较高的效率。在实现过程中,DA算法通常会配合LCP数组(最长公共前缀数组)一起使用,...
总的来说,后缀数组和LCP数组是处理字符串问题的强大工具,它们的构造算法巧妙地利用了字符串的特性,实现了高效的数据组织和查询。在编程竞赛和实际应用中,掌握这些概念和算法对于解决字符串相关的复杂问题至关...
3. **倍增算法**:如附件中的“附件1---倍增算法和dc3算法完整代码”所示,倍增算法是一种高效的构造方法,通过多次短字符串排序逐步构造出整个后缀数组,时间复杂度可达到O(n log n)。 4. **DC3算法**(Doubly-...
相比于后缀树,后缀数组在实现上更为简单,同时在时间和空间效率上也有良好的表现。 ### 基本定义 后缀数组是将一个字符串的所有后缀按照字典序排序后形成的数组。例如,对于字符串 "abcba",其后缀有 "abcba", ...
后缀数组的构建是通过特定算法实现的,如O(nlogn)复杂度的倍增算法,它有效地利用了后缀间的关联,避免了直接排序带来的高时间复杂度。 首先,我们理解一下相关概念: 1. 字符集:一个有序的字符集合,例如ASCII或...
倍增算法与DC3算法虽然都是构造后缀数组的有效方法,但它们在实现的复杂度、时间效率等方面各有特点。倍增算法更适合处理单个或少数几个大字符串的情况,而DC3算法在处理多个小字符串时可能更为高效。通过对比分析,...
尽管后缀数组的某些方面不如后缀树强大,但在空间复杂度和实现简洁性方面,它具有明显的优势,这使得后缀数组在实际应用中具有广泛的应用前景。随着算法研究的不断深入,我们可以预期后缀数组会在更多的领域中找到其...
后缀数组的构造可以使用朴素算法或倍增算法,朴素算法的时间复杂度为 O(n2),倍增算法的时间复杂度为 O(nlogn)。 后缀数组的构造是指将字符串的所有后缀排序后的结果储存在一个数组中。后缀数组的每个元素 sa[i] ...
后缀数组与后缀树的对比则展示了两者在性能和实现上的差异,后缀数组在性能相近的同时,可提供更简洁高效的实现方案。 总之,后缀数组作为一种强大的字符串处理工具,在计算机科学领域中有着广泛的应用。它不仅能够...
倍增算法对后缀数组构造,height数组构造,lcp构造及O(P+log(n))的字符串搜索,可以运行的源代码,具体对应的算法可在我的博客中查看。
相较于后缀树,后缀数组不仅实现起来更加简便,而且在时间和空间复杂度方面表现优异,使其成为信息学竞赛及实际应用中的首选工具。本文将详细介绍后缀数组的概念、构建方法及其应用场景。 #### 基础概念 - **字符...