后缀数组总结
2009-10-25 09:05
后缀数组是处理字符串的有力工具,后缀数组可以解决大多数后缀树解决的问题,由于它的实现要比后缀树简单,因此深受广大ACM爱好者的喜爱,当然还是有一些问题只有后缀树能解决的问题,等学习了后缀树再将其添上。后缀数组最常用的是求取最长公共前缀。
后缀suffix数组,suffix【i】表示从第i个字符开始的后缀;
后缀数组sa,保留1~n的某个排列,保证suffix【sa【i】】<suffix【sa【i】】,可以证明任何从不同位置开始的后缀不可能相等,这里不再证明。
名词数组:名词数组rank【i】保存的是suffix【i】在所有后缀中从小到大排列的名次。简单的说,后缀数组是“排第几的是谁?”,名次数组时“你排第几?”。容易看出,后缀数组和名次数组互为逆运算。
height数组,height【i】表示suffix【sa【i-1】】和suffix【sa【i】】的最长公共前缀,实际中往往应用最多的就是最长公共前缀,用其求取必要的东西。
后缀数组可以解决的问题:
一、单个字符串的问题:
(1) 重复字串
例1:可重叠最长重复子串
给定一个字符串,求最长重复子串,这两个子串可以重叠。
算法:只要求出最大的height就可以了。
例2:不可重叠最长重复子串(pku 1743)
给定一个字符串,求最长重复子串,这两个子串不能重叠。
算法:二分答案,变成判定性问题,二分代码和判定部分的代码,个人认为最重要的部分:
int le=0,ri=nm-1;
while(le<ri)
{
int mid=(le+ri+1)/2;
if(ok(mid,nm)) le=mid;
else ri=mid-1;
}
最后结果就是le+1;
int ok(int len,int nm)
{
int i,mx=0,mi=nm;
for(i=0;i<nm;i++)
{
if(h[i]<len){ mx=0;mi=nm;}
else
{
mx=max(mx,sa[i]);
mx=max(mx,sa[i+1]);
mi=min(mi,sa[i]);
mi=min(mi,sa[i+1]);
}
if(mx-mi>len) return 1;
}
return 0;
}
例2:可重叠的k次最长重复子串(pku 3261)
给定一个字符串,求出至少出现k次的最长重复子串,这k个子串可以重叠。
还是二分判定了。
判定部分的代码:
int appk(int x,int k,int n)
{
int app=1,i;
for(i=0;i<n;i++)
{
if(h[i]>=x)
{
app++;
if(app==k)
return 1;
}
else
app=1;
}
return 0;
}