后缀数组是真的难,看了一整天并没有理解掉,不过能用了大概,能用的意思大概就是:跳过理解求后缀数组的深切内涵而直接套用模板,使用后缀数组来解决问题。没错,后缀数组很难,但是用这些论文上的一句话来形容他的话,解决字符串问题的强有力的工具,工具,的意思就是,的精髓在于使用,而不是用什么工具都要造一个。这算是给自己的菜找借口了啦,其实这样说起来是舍本逐末了,偏离了学算法的初衷,真的尽力了,也就理解了大意,细节上完全混乱,只能等有缘从那个题里灵光乍现了。
还是要说说对后缀数组的理解:前面学了字典树、ac自动机,实际上就是一个前缀树嘛,那么后缀可不可以建树呢,没道理不可以,所以也有后缀树这种东西,但是后缀树和前缀树差别甚大,在一篇ppt上有些讲解,他的每个节点是一个字符串而不是一个字符(为什么?不知道?反正意思就是很难很复杂),所以我们来学一个简单的后缀数组吧,这可是简单好操作好理解完全不虚后缀树的好东西哦。。。
后缀树的大意就是把串的后缀排序,没了。说出来我才发现这么简单,没错,好像一个sort(nlogn)能够解决的事情,但是strcmp什么的非线性,不能这么算的。所以选择其他方法倍增算法。求出来sa【】(第几是哪个后缀),rank【】(这个后缀排第几)两个数组。
倍增算法的意思啊内容啊就是我理解不透的东西了,大意就是。。。。。预处理第一个字母后,如下图:
涉及到新内容:基数排序。
然后有了这个后缀数组好像没什么意思啊,所以说,工具,得用啊。然后就加上了LCP(最长公共前缀),及新数组height【】。再然后根据他的性质转化为了RMQ问题,以及RMQ的新模板(ST、标准)。这样就结束了模板之路。
模板代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000000+100;
struct SuffixArray
{
char s[maxn];
int sa[maxn], rank[maxn], height[maxn];
int t1[maxn], t2[maxn], c[maxn];
int dmin[maxn][20];
int n;
void build_sa(int m)
{
int i, *x=t1, *y=t2;
for(i=0; i<m; i++)
c[i]=0;
for(i=0; i<n; i++)
c[x[i]=s[i]]++;
for(i=1; i<m; i++)
c[i]+=c[i-1];
for(i=n-1; i>=0; i--)
sa[--c[x[i]]]=i;
for(int k=1; k<=n; k<<=1)
{
int p=0;
for(i=n-k; i<n; i++)
y[p++]=i;
for(i=0;i<n;i++)
if(sa[i]>=k)
y[p++]=sa[i]-k;
for(i=0; i<m; i++)
c[i]=0;
for(i=0; i<n; i++)
c[x[y[i]]]++;
for(i=1; i<m; i++)
c[i]+=c[i-1];
for(i=n-1;i>=0;i--)
sa[--c[x[y[i]]]]=y[i];
swap(x, y);
p=1;
x[sa[0]]=0;
for(i=1; i<n; i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?p-1:p++;
if(p>=n)
break;
m=p;
}
}
void build_height()//n不能等于1,否则出BUG
{
int i,j,k=0;
for(i=0; i<n; i++)
rank[sa[i]]=i;
for(i=0; i<n; i++)
{
if(k)
k--;
j=sa[rank[i]-1];
while(s[i+k]==s[j+k])
k++;
height[rank[i]]=k;
}
}
void initMin()//min[i, j]表示从第i个数起连续2^j个数中的最大值
{
for(int i=1; i<=n; i++)
dmin[i][0]=height[i];
for(int j=1; (1<<j)<=n; j++)
for(int i=1; i+(1<<j)-1<=n; i++)
dmin[i][j]=min(dmin[i][j-1] , dmin[i+(1<<(j-1))][j-1]);
}
int RMQ(int L, int R)//取得范围最小值
{
int k=0;
while((1<<(k+1))<=R-L+1)
k++;
return min(dmin[L][k] , dmin[R-(1<<k)+1][k]);
}
int LCP(int i, int j)//求后缀i和j的LCP最长公共前缀
{
int L=rank[i], R=rank[j];
if(L>R)
swap(L,R);
L++;//注意这里
return RMQ(L, R);
}
}sa;