摘要:
树状数组(BIT - Binary Indexed Tree)是一个用数组表示的树型数据结构,最早用于频次累计表中。树状数组中每个元素维护一个频次表A的特定部分区段的累加和。假设输入动态序列为A,A中元素值可以被修改,如果使用直接蛮力法来查询一个区间i..j的累加和,复杂度为O(n),而树状数组由于每个元素存储的是部分累加和,通过数组下标的二进制索引规律可以在O(logn)时间内找到1...j或i...j的累加和。存储空间只需要维护树状数组T,A的元素值可以很容易地从T得到(即特例i=j时的区间查询)。数组A下标通常从0开始,而树状数组的有效下标是从1开始。下面关于lowbit的讨论假设数组A 阅读全文
摘要:
在普通RMQ问题的<O(n),O(1)>算法中,由于需要构造Cartesian Tree和得到Euler tour,两个2*n-1大小的数组E和L使得空间消耗增加O(4*n)。本文介绍的Fischer-Heun算法绕过构建Cartesian Tree的步骤,也不需要将普通RMQ转化为RMQ+1/-1问题再求解。该算法的基本思想也是采用RMQ+1/-1的分组处理, 分别生成组内的Lookup Table和组外的稀疏表。但是在每个组的大小缩小了一倍,成为s=(logn)/4。由于每个组对应一颗笛卡尔树,若两个不同的组对应的两棵笛卡尔树相同的话,那么这两个组的lookup table完全 阅读全文
摘要:
基本思想是通过对问题的转化,最终得到<O(n),O(1)>时间复杂度。该算法分以下两大步骤:1)将RMQ问题转化为LCA问题:先构建输入数列A的笛卡尔树,构建笛卡尔树的复杂度为O(n)。2)将LCA问题转化为RMQ+1/-1问题:通过对笛卡尔树的DFS遍历得到欧拉路径(Euler Tour),建立三个数组E,L和R。其中E和L大小是2*n-1,E中元素表示笛卡尔树中每个结点的label值(实际上就是数列A的下标索引值),L中元素对应欧拉路径上每一个被访问结点的深度(即从root到该结点的深度,root本身的深度为0); R是笛卡尔树中的每一个结点第一次被访问时在E中的位置,即E[R 阅读全文
摘要:
JavaCV(http://code.google.com/p/javacv/) is a java wrapper of OpenCV (http://opencv.willowgarage.com/). Because it is just a java wrapper, it is necessary to install opencv as a prerequisite. As of this writing, javaCV requiresOpenCV 2.3. Here is how to prepare the whole setup before we get to play 阅读全文
摘要:
RMQ+1/-1问题要求数列中相邻两个元素相差+1或-1。利用这个限定条件可以使该算法复杂度总体上达到<O(n),O(1)>。具体做法是:1) 设数列A的大小为n,先对数列A分组,每组大小为b=1/2.logn (之所以这样分是为了将预处理复杂度从O(nlogn)降为O(n)),共分为n/b个组;以下第2到第4步完成RMQ+1/-1问题的预处理阶段(参考以下实现中的preprocess方法)。2)生成O(sqrt(n))个LU表P[][]和一个block类型数组T[]: 对每个组内部进行预处理(inblock preprocessing)(参考以下实现中的makeLUTable方法 阅读全文
摘要:
ST(Sparse Table)算法的基本思想是,预先计算从起点A[i]开始长度为2的j次方(j=0,1...logn)的区间的最小值,然后在查询时将任何一个区间A[i..j]划分为两个预处理好的可能重叠的区间,取这两个重叠区间的最小值。在预处理阶段,从起点A[i]开始,任何一个长度为2^j的区间都可以划分为两个长度2^(j-1)的区间,其中第一个区间的范围为:i...i+2^(j-1)-1;第二个区间的范围为:i+2^(j-1)...i+2^j-1。用M[i,j]表示从A[i]开始,长度为2^j的区间(即A[i]...A[i+2^j-1])最小值对应的下标,那么A[M[i,j]] = min 阅读全文
摘要:
RMQ(Range Minimum Query)问题是计算一个输入数列A[0...n-1]从位置i到位置j之间的最小值,即RMQ[i,j]=min{A[k], k=i,i+1...j}。RMQ的解法有很多,比如Sparse Table(ST)算法(注意这个ST缩写不是指Segement Tree哦)和转化为特殊的+1/-1 RMQ的算法。为了查询的方便,RMQ算法需要对数列A进行预处理(preprocessing),如果用<f(n),g(n)>分别表示RMQ算法的预处理复杂度和查询复杂度的话,用线段树(segment tree)解决RMQ问题的复杂度为<O(n),O(logn 阅读全文
摘要:
设模式串的长度为m,文本的长度为n,使用后缀数组做文本匹配,如果只用后缀表suftab和折半查找算法的复杂度为O(m*logn); 如果使用最长公共前缀表lcptab和折半查找算法,复杂度可以降至O(m+logn);使用增强型后缀数组(ESA)表childtab,复杂度为O(m)。本文使用复杂度为O(m)的算法,在匹配之前要求先构造SA(下面采用DC3算法构造后缀数组),然后计算出后缀数组的suftab,lcptab和childtab。由于通过childtab可以在O(1)时间复杂度内找到每一个lcp-interval的所有child-interval,因此这跟后缀树的自顶向下匹配模式串的算法 阅读全文
摘要:
相比后缀树,后缀数组的优势是存储空间小,相关算法效率高。但是若存放childtab还是使用up,down和nextLIndex三个属性,这显然不符合后缀数组节省空间的"第一原则"。幸运的是,可以压缩存储childtab,将三个属性up,down和nextLIndex变成一个一维数组。后缀数组childtab压缩存储的基本思路是保留所有的nextLIndex值(因为nextLIndex没有冗余),将大部分冗余的down值剔除(剩下部分的down值存放到空白的nextLIndex位置中),然后将up值放到空白的nextLIndex位置。首先给出lcp-interval和chil 阅读全文
摘要:
后缀数组自底向上遍历等价于后缀树的自底向上遍历。由于后缀数组不是树型结构,在遍历时除了SA本身之外还需要额外的信息,这时Suffix Array就是一个增强的后缀数组(Enhanced Suffix Array)了。该算法使用后缀数组的一个增强信息---LCP表,并通过堆栈模拟自底向上的遍历。遍历的结果就是一颗虚拟的lcp-interval树,其中每一个结点对应后缀树的一个内部结点。有些应用中,遍历时需要知道每个结点的孩子信息,因此在下面的实现中提供了两个版本bottomUpTraverseWithoutChildren和bottomUpTraverseWithChildren。需要说明的是, 阅读全文