复杂度
1、求后缀数组,用的二分查找法和基类比较,所以时间复杂度是 n*lg2n,只保存后缀的位置,空间复杂度是n
2、比较查找,没有公共部分的情况,str1排名的最小值>str2排名的最大值 或者 str1排名的最大值>str2排名的最小值,就认为没有公共部分,复杂度为2
3、比较查找,有公共部分的情况,按照n、m从小到大的顺序查找的,复杂度为n+m
所以:
时间复杂度是 O(2n*logn)和O(2n*logn+2n)之间,logn的底数是2
空间复杂度是O(2n)
什么是后缀数组
1、给字符的后缀排好序的数组
2、保持的值是字符的后缀
3、排序规则是基类排序,从小到大
后缀
后缀就是从字符串的某个位置i到字符串末尾的子串,我们定义以s的第i个字符为第一个元素的后缀为suff(i)
例如
字符 s='123456',它的后缀数组表示为[ 0, 1, 2, 3, 4, 5 ],也即是 [123456,23456,3456,456,56,6]
字符 s='热点112',它的后缀数组表示为[ 2, 3, 4, 1, 0 ],也即是[112,12,2,点112,热点112]
算法思路
1、对字符str1进行排序,求出后缀数组sa1,对字符str2进行排序,求出后缀数组sa2
2、设 n表示字符str1的排名,m表示字符str2的排名,初始设 n=0,m=0
sa1[n]表示排名为n的字符str1位置
sa2[m]表示排名为m的字符str2位置
逻辑部分
初次查询从n=m=0开始,从小到大比较位置sa1[i]和sa2[j],如果字符相同,则求出相同部分的长度len,更新查询起始位置 n=i,m=j
下次查询,从n、m位置从小到大继续比较位置sa1[i]和sa2[j],如果字符相同,则求出相同部分的长度len,更新查询起始位置 n=i,m=j
如果 n 3、此时按照排名顺序求出了所有的公共部分,还需要去重 因为后一个字符长度,必然比前一个字符长度小,所以判断当前和上一个有没有重叠,重叠的话就去掉当前,保留了最长的公共子串 4、返回的数据格式如下,按照str1位置顺序排列的 [
[str1位置i、长度len、str2位置j],
[str1位置i、长度len、str2位置j]
]
//查找
function find(str,hasSortArr,callback) {
let l=0,r=hasSortArr.length;
let index=-1;
if(hasSortArr.length>0){
const ri=callback(str,hasSortArr[r-1]);
if(ri===1){
return [r,-1]
}else if(ri===0){
return [r-1,r-1]
}else{
r=r-1;
}
const li=callback(str,hasSortArr[0]);
if(li===-1){
return [0,-1]
}else if(li===0){
return [0,0]
}else{
l=l+1;
}
while(r-l>0){
const m=(l+r)>>1;
//比较下坐标大小
const order=callback(str,hasSortArr[m])
if(order===1){
l=Math.max(l+1,m)
}else if(order===-1){
r=Math.min(r-1,m)
}else{
l=r=m;
index=m;
}
}
}
return [(l+r)>>1,index]
}
//SA[i]表示排名为i的后缀下标、rk[i]表示起始位置的下标为i的后缀的排名
function getSa(str) {
const sLen=str.length;//总共排名长度
//排名函数
const compare=function (n1,n2) {
let dis=0;
let len=0;
while (dis===0){
//超过字符,返回小于
if(n1+len===sLen){
dis=-1
}else if(str[n1+len]>str[n2+len]){
dis=1;
}else if(str[n1+len]