后缀数组增量包算法

//压缩增量包
function MinChunk(chunkArr,s1) {
  var minChunk=[]
  for(let i=0;i<chunkArr.length;i++){
    let arr=chunkArr[i];
    if(arr[0]==='e'&&arr[2]<String(arr[1]).length+String(arr[2]).length){
      arr=['a',s1.substr(arr[1],arr[2])]
    }
    var lastArr=minChunk[minChunk.length-1];
    if(arr[0]==='a'&&lastArr&&lastArr[0]==='a'){
      lastArr[1]=lastArr[1]+arr[1];
    }else{
      minChunk.push(arr)
    }
  }
  const narr=[]
  for(let i=0;i<minChunk.length;i++){
    let arr=minChunk[i];
    if(arr[0]==='a'){
      narr.push(arr[1])
    }else if(arr[0]==='e'){
      narr.push([arr[1],arr[2]])
    }
  }
  return narr;
}

//比较两字符的相等长度和大小
function compareLen(n1,n2,str1,str2) {
  //求出相等部分
  var len=0;
  while (n1+len<=str1.length&&n2+len<=str2.length&&str1.charCodeAt(n1+len)===str2.charCodeAt(n2+len)){
    len++;
  }
  //求出大小
  var dis=0;
  if(n1+len===str1.length){
    dis--
  }
  if(n2+len===str2.length){
    dis++
  }
  if(dis===0&&n1+len<str1.length&&n2+len<str2.length){
    if(str1.charCodeAt(n1+len)<str2.charCodeAt(n2+len)){
      dis--
    }else{
      dis++
    }
  }
  return [len,dis];
}

//查找字符在数组的最大相等长度和大小
function findLen(str,hasSortArr,callback) {
  var l=0,r=hasSortArr.length;
  var lock=-1;
  var len1=0,len2=0;
  var len=0,dis=0;

  if(hasSortArr.length>0){
    [len1,dis]=callback(str,hasSortArr[0]);
    if(dis<1){
      return [0,len1,dis]
    }else{
      l=l+1;
    }
    [len2,dis]=callback(str,hasSortArr[r-1]);
    if(dis>-1){
      return [r-1,len2,dis]
    }else{
      r=r-1
    }

    if(l===r){
      if(len1>len2){
        return [r-1,len1,dis]
      }else{
        return [r,len2,dis]
      }
    }
    while(lock===-1){
      var m=(l+r)>>1;
      //比较下坐标大小
      [len,dis]=callback(str,hasSortArr[m])
      if(dis===1){
        if(l+1===r){
          if(len<len2){
            lock=r;
            len=len2;
            dis=-1
          }else{
            lock=m;
          }
        }else{
          len1=len;
          l=m
        }
      }else if(dis===-1){
        if(l+1===r){
          if(len<len1){
            lock=l;
            len=len1;
            dis=1
          }else{
            lock=m;
          }
        }else{
          len2=len;
          r=m
        }
      }else{
        lock=m;
      }
    }
    return [lock,len,dis]
  }
  return [0,0,1]
}

//SA[i]表示排名为i的后缀下标、rk[i]表示起始位置的下标为i的后缀的排名
function getSa(str) {
  var sLen=str.length;//总共排名长度
  //后缀数组
  var sa=[];
  for(var i=0;i<sLen;i++){
    var [n,len,dis]=findLen(i,sa,function (n1,n2) {
      return compareLen(n1,n2,str,str)
    })
    if(dis===1){
      sa.splice(n+1,0,i)
    }else{
      sa.splice(n,0,i)
    }

  }
  return sa
}
//用后缀数组,求两个字符的公共子串,也就是两个文件的公共部分
function makeChunk(s1,s2){
  var sa1=getSa(s1);//后缀数组,排序
  const chunkArr=[]
  let i=0;
  while (i<s2.length){
    var [n,len,dis]=findLen(i,sa1,function (n2,n1) {
      return compareLen(n2,n1,s2,s1)
    })
    if(len>0){
      chunkArr.push(['e',sa1[n],len])
      i=i+len;
    }else{
      chunkArr.push(['a',s2[i]])
      i++
    }
  }
  return chunkArr;
}
//执行增量包
function execChunk(s1,chunk){
  var ns='';
  for(var i=0;i<chunk.length;i++){
    var arr=chunk[i];
    if(Object.prototype.toString.call(arr)==='[object Array]'){
      ns=ns+s1.substr(arr[0],arr[1])
    }else{
      ns=ns+arr
    }
  }
  return ns;
}
module.exports=function (s1,s2) {
  const chunk=makeChunk(s1,s2);

  const chunkArr=MinChunk(chunk,s1)
  if(execChunk(s1,chunkArr)!==s2){
    throw '增量包打包错误'
  }
  return chunkArr
};

  

原文地址:https://www.cnblogs.com/caoke/p/13691520.html