后缀数组增量包算法
//压缩增量包 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 };