06 2011 档案
摘要:后缀树(Suffix Tree)是一种特殊的Trie,它的用途非常广泛,其中一个主要的应用是作文本匹配,也像KMP等算法一样,它也是空间换时间的一个典范。利用 Suffix Tree做文本匹配与其他的模式匹配算法比如KMP和Boyer-Moore算法的主要区别是,后缀树文本匹配算法是对文本T做预处理,而KMP算法是对模式串P做预处理。因此后缀树常用于文本静态,而模式串动态的场合;而KMP等算法常用于文本动态,模式串静态的场合。设T的长度为n,P的长度为m,一般情况下m<n。在预处理中,用Suffix Tree匹配的复杂度为O(n),而KMP和Boyer-Moore的复杂度为O(m)。可是
阅读全文
摘要:问题:有N个字符串,计算它们公共的最长连续子串。例如,字符串集合{abcd,bcca,aabc},它们公共的最长连续子串是bc。又如{abcfd,bcabca,aabcf}的公共的最长连续子串是abc。针对本问题,可以用特殊的字符(该字符必须是N个串中没有出现的字符)将N个字符串连起来形成一个新串,然后构建这个新串的后缀树。比如对字符串集合 {abcd,bcca,aabc},可以连成新串abcd$bcca@aabc%,其中子串之间的分隔符为 '$','@','%'。满足条件的最长子串就是最深的分叉结点,而且该分叉结点下面的后缀的suffix(注意
阅读全文
摘要:原题:http://acm.hdu.edu.cn/showproblem.php?pid=1671Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Problem DescriptionGiven a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let’s say the phone catalogue listed
阅读全文
摘要:原题: http://poj.org/problem?id=3002DescriptionBill has found the perfect way to make money playing the slot machines. After months of careful research, he has finally figured out the mechanics behind how the machines operate. Now he is ready to make profit of his findings.But first an introduction to
阅读全文
摘要:原题:http://topic.csdn.net/u/20110623/10/0324c75e-f72a-4894-83e9-f5a00fc88f62.html?85618描述:有k个正整数 任取其中k-1个数 找到一个他们的最小公倍数数N 求N的最小值分析:可以采用以下三种方法:******************************1) 方法一:(缺点:需对k个数做质因素分解)基本想法是先对每个数做质因素分解,然后对k-1个数两两相互统计,找出它们对应质因子的最大个数,最后当所有k-1个数统计结束时,结果就是该质因子个数的最大值,然后将这些质因子相乘(个数>=1,需要重复相乘),
阅读全文
摘要:原题:http://acm.hdu.edu.cn/showproblem.php?pid=1251Problem DescriptionIgnatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).Input输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.注意:本题只有一组测试数据,处理到文件结束.Output对于每个提问,给出以该字
阅读全文
摘要:Patricia前缀树(Patricia Trie)及其基本操作Trie是一种字典树,用于存储文本字符,并利用了单词之间共享前缀的特点,所以叫做前缀树。不像平衡BST,Trie的高度只与最长的文本串的长度s有关系,而与单词的数量n无关。Trie的节点分两种:内部结点和叶子结点,内部结点用来存储单词key的成分字母,如果设字母表大小为d,那么每个内部结点最多有d个孩子,叶子结点存储该单词作为key的数据内容(data)。注意内部结点和叶子结点并不是互斥的,一个内部结点本身可以有儿子结点,同时它也可以是一个叶子结点。例如:这里ab结点本身就是叶子结点,因为它以#结束符标记了,同时它有两个儿子结点a
阅读全文
摘要:Boyer-Moore除了考虑Horspool算法(参考笔者的另一篇专门介绍Horspool算法的文章)的坏字符之外,还将模式串中已经匹配成功的后缀(叫做好后缀, good suffix)考虑进来,从而得到全部已经知道的启发信息(heuristic)。因此从理论上来说,BM算法应该是性能最佳的一个算法,实践中也证明了这一点。 这也是为什么BM算法经常用作精确匹配算法里面的性能测试基准算法。例如,在通过下面的图示就可以看出, KMP算法由于没有考虑进来bad character信息,比较次数比BM算法稍多: (图一)上面在i=4,j=4时出现mismatch,在KMP算法中的做法是找出j-1右边
阅读全文
摘要:KMP(Knuth–Morris–Pratt)算法的发明时间几乎跟BM(Boyer-Moore)算法在同一时期,即上世纪70年代末(巧合的是随着互联网的发展对文本处理提出了更高的要求,从而最近几年字符处理又成了热门话题),二者在最坏情况下的时间复杂度都是O(n)。它与BM算法的主要区别是:1)在每次匹配中都是从左到右匹配,BM算法每一次匹配过程都是从模式串末尾开始匹配(指针从右到左移动),直到发现匹配失败字符(mismatch)才根据两张表(好后缀位移表-good suffix shift table和坏字符位移表-bad character shift table)决定向右移动一定的位置,因
阅读全文
摘要:英语里有句习语叫"find a needle in a haystack",译成中文叫"大海捞针"(原意是在一堆干草中寻找一根针)。计算机中的文本匹配(string matching)就是要解决怎样在一段很长的文本中找到符合要求的一个子串,该子串通常叫模式串(pattern),也就是对应我们要找的“针”。常用的文本精确匹配(exact string matching)算法有蛮力法(brute-force),Boyer-Moore算法和KMP算法。后两种算法都是用空间换 时间的经典案例,不像蛮力法,文本的位置指针i不需要回退到已经匹配过的字符位置(BM算法
阅读全文
摘要:原题出自:http://topic.csdn.net/u/20110617/10/f9b370a2-dad8-42ad-9202-86e23cd84dea.html问题描述:小华和小明的风筝不小心断了线,掉到了一个圆形湖圆心处的孤岛上。两人都不会游泳,幸好湖边有一条小船,不过可惜没有桨,也没有其他的动力工具。船上放着一根绳子,两人决定利用这根绳子,将一头栓在船上,小华上船,小明在岸边拉绳子使船移动的方法,靠近小岛,由小华取回风筝,再由小明把船拉回岸边。如果他们最后成功了,请问这根绳子最少要有多长(栓在船上的部分不计)?他们是怎么做到的?分析:设湖的圆心为O,湖的半径为r。显然绳子长度>=
阅读全文
摘要:AA树 - 红黑树的变种作者:ljs 2011-06-15AA树是Arne Andersson教授在他的论文"Balanced search trees made simple"中介绍的一个红黑树变种,设计的目的是减少RB树考虑的cases。AA树是一颗红黑树,但是规定红色结点不能作为任何结点的左孩子,也就是说红色结点只能作为右孩子。这样本质上跟2-3树类似(虽然后者属于B树)。另外AA树为实现方便,不再使用红黑两种颜色,而是用level标记结点。level实际上就相当于RB树中的black height,叶子结点的level等于1(反过来,level等于1的不一定是叶子结
阅读全文
摘要:原题:1) ZOJ: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=21362) POJ: http://poj.org/problem?id=2533问题描述:A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK), where 1
阅读全文
摘要:由于项目需要在.net下将pdf转换为普通图像格式,在网上搜了好久终于找到一个解决方案,于是采用拿来主义直接用。来源见代码中注释,感谢原作者。using System;using System.Collections.Generic;using System.Text;using System.Runtime.InteropServices;using System.Collections;/**Convert PDF to Image Format(JPEG) using Ghostscript API convert a pdf to jpeg using ghostscript comm
阅读全文
摘要:堆排序需先建立堆,然后对堆进行操作,每次取根节点值,将它与数组右边位置(即叶子节点)的值交换,交换后完全二叉树的叶子节点数减1,由于此时可能破坏堆的性质,需要向下将新的根节点与它的后代节点交换,直到叶子节点。首先建立堆的过程可以采用Floyd算法(heapify):完全二叉树(complete binary tree)和二叉堆都可以用一维数组表示用层序(level order)遍历的结果。Floyd算法将数组表示的任意一颗完全二叉树转换为堆,它无需开辟额外的空间,直接在数组中交换数据(即in-place做法),转换成堆结构,它的时间复杂度只有O(n),而不是预想的O(nlogn)。具体做法是自
阅读全文
摘要:伸展树(splay tree)是一种能自我调整的二叉搜索树(BST)。虽然某一次的访问操作所花费的时间比较长,但是平摊(amortized) 之后的访问操作(例如旋转)时间能达到O(logn)的复杂度。对于某一个被访问的节点,在接下来的一段时间内再次频繁访问它(90%的情况下是这样的,即符合90-10规则,类似于CPU内或磁盘的cache设计原理)的应用模式来说,伸展树是一种很理想的数据结构。这是因为最近被访问的节点一直位于根节点的附近,从而再次被访问时的搜索路径长度比较小。这点与平衡的二叉树(比如AVL树和红黑树)不一样。另外一点与其他平衡二叉树的区别是,伸展树不需要存储任何像AVL树中平衡
阅读全文
摘要:插入排序的基本思想是每次取右边的第一个元素,将它与左边已经排好序的元素一一比较,如果该元素比第j个元素大,就将该元素插入到第j和第j+1个元素之间。最坏的情况下(比如输入序列是个逆序),每个元素都需要跟左边已经排好序的所有元素比较,总的比较次数为:1+2+...+(n-2)=(n-1)(n-2)/2 ~ O(n^2); 插入操作需要将插入位置右边的元素平移一个位置,总得移动操作次数也为O(n^2)。插入排序是稳定的(stable),这与选择排序不同(选择排序用的是交换非相邻元素位置,所以不稳定),并且如果原序列基本上排好序的情况下,比较和移动操作的次数可以大大降低,因为它只需比较一部分元素,在
阅读全文
摘要:冒泡排序的基本思想是每次在左边未排好序元素序列中依次挨个比较大小,如果相邻两个元素不是正确顺序(正确顺序为左边元素小于右边元素),交换该两个元素。直到左边未排好序的元素中最大的元素进入(冒泡)右边已经排好序的元素中,并作为最小元素落在已经排好序的元素序列的最左端。总的比较操作次数为:(n-1) + (n-2) + ... + (1)=n(n-1)/2; 最坏的情况总的交换次数等于总的比较操作次数, 比如输入的元素为从大到小顺序,需要每次比较(if(A[j-1]>A[j]))完之后做一次交换。在下面的实现,考虑到每一次外层循环中,如果内层循环中没有发生任何交换(exchanged = fa
阅读全文
摘要:选择排序的基本思想是每次从右边未排好序的元素中找出最小的元素,然后将该元素和左边已经排好序的元素中最大的元素(它位于左边元素序列的最右端)交换,这样每次交换都将一个元素排好序。 总的比较操作次数为(n-1) + (n-2) + ... + (1)=n(n-1)/2;总的交换操作为n-1(所以相对其他的排序算法,它的交换操作次数比较少)。注意选择排序的一个缺点是,它是不稳定的(unstable),比如对序列(a,b,c,d),设a=b,d = min(b,c,d),那么第一次选择到最小元素d之后需要交换:a<->d,得到的结果为(d,b,c,a),因为a=b,原来a和b的位置(a..
阅读全文
摘要:爬楼梯问题:一段楼梯共n级台阶,每次只能走一级、两级或三级台阶,问共有多少种走法?列出n=10时的所有走法。分析:首先考虑第一步的走法:第一步可以跨1级台阶,也可以跨2级和3级台阶。设S(n)表示走n级台阶的走法数量。如果第一步跨1级台阶,剩下的台阶数为n-1, 也就是说这种情况下(即第一步跨1级台阶的)走法相当于S(n-1);如果第一步跨2级台阶,剩下的台阶数为n-2,也就是说如果第一步跨2级台阶的走法相当于S(n-2);如果第一步跨3级台阶,剩下的台阶数为n-3,也就是说如果第一步跨3级台阶的走法相当于S(n-3)。从而得到递推关系式:***************************
阅读全文
摘要:参考原文(原文作者:Timothy J. Rolfe):http://penguin.ewu.edu/~trolfe/DSWpaper/DSW算法用于平衡BST(二叉查找树),而且平衡后的二叉树是一颗完全二叉树(complete binary tree-即叶子节点顶多位于最后两层,而且从左到右排列)。该算法的优点是:无须开辟额外的空间用于存储节点,时间复杂度线性O(n)。该算法分两个阶段进行:首先将任意一颗二叉树通过一序列右旋转(right rotations)转换成一个单链结构(称作vine,即每个非叶子节点只有右孩子,没有左孩子);然后在第二阶段中对单链结构通过几个批次左旋转(left r
阅读全文
摘要:AC解 - 用动态规划解决一道排列组合计数问题(序关系计算)原题如下:http://acm.nankai.edu.cn/problem.php?problem=1134[同时请参考网友pandm发的帖子:http://topic.csdn.net/u/20110525/01/adf4d0b0-2b8e-4c0a-b8da-07b27f1711cc.html?seed=589816236&r=73524109#r_73524109]There are 13 possible orderings for three numbers, if we sort them with the rel
阅读全文
摘要:不使用递归遍历二叉树有几种方法:迭代,线索二叉树和Morris算法(通过临时转换二叉树变成一个类似链表的结构)。下面是迭代方法和递归的对比,后面单独列出Morris算法的实现。迭代方法和递归的对比实现:import java.util.Stack;/** * * Using recursive & iterative(ie. non-recursive) methods to traverse binary tree in * inorder, preorder and postorder * * @author ljs * 2011-05-25 * * */public class
阅读全文
摘要:如果用一颗二叉树表示加减乘除及0-9的数字构成的数学表达式,对二叉树进行后序遍历得到的就是后缀表达式。后缀表达式可以通过堆栈直接计算其值。如果输入的是中缀表达式,下面的方法toBinaryTree()将它转化为二叉树。代码:import java.util.ArrayList;import java.util.List;import java.util.Stack;/** * * @author ljs * 2011-05-24 * * 把中缀表达式转换成一棵二叉树,然后通过后序遍历计算表达式的值 * 1. unary operator like -3 is not supported * 2
阅读全文
摘要:在我的另外一篇文章"正整数中数字1的计数问题(上)"中,实现了一个简单的算法来计算f(n)。该算法由于只考虑相邻两个数的变化规律,因此在计算单个长整数时(比如n=911111111099999009L),可能要很长时间才能算完,显然该算法适用范围比较狭小。本文继续围绕那道Google面试题,探讨一个计算单个值f(n)的快速算法以及如何求出所有满足f(i)=i这样的整数(对很大的数计算时间能达到毫秒数量级)。分析:首先注意到,比如要计算i=3260这样的f(i),在算到i=3200的时候,其实又重复了以前的计算,即重新计算1到60的值。本算法就是基于这个考虑,将一个数拆成左右
阅读全文
摘要:正整数中数字1的计数问题(上)原题出自Google的一道比较老的面试题:Consider a function which, for a given whole number n, returns the number of ones required when writing out allnumbers between 0 and n. For example, f(13)=6. Notice that f(1)=1. What is the next largest n such that f(n)=n?本实现没有利用可能存在的数学公式,只考虑相邻两个数得变化规律,因此运行效率并不理想。
阅读全文
摘要:(原题出自微软公司面试题)问题如下:有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。例如: var a=[100,99,98,1,2, 3];var b=[1, 2, 3, 4,5,40];分析:通过交换的方式,最终的状态是在保证两个序列中元素个数相同的条件下,任何一个元素都可以位于两个序列中的任何一个。这样问题可以转化为:在一个长度为2*n的整数序列中,如何将元素个数分成两个子集,记每个子集的元素之和分别为S1和S2,使得|S1-S2|最小。显然这是一个最优化问题,如果用brute-force方法,
阅读全文
摘要:二进制方法中,只需要移位(<<和>>)和加减操作(+和-),不像欧几里德算法中需要乘法和除法运算。虽然算法效率更高,但是程序的可读性和可维护性差一些。如果设d=gcd(u,v) = u.x + v.y, 本算法涉及到六种操作:1)已知ext_gcd(u,v)如何求ext_gcd(u,2v)=u'.x' + v'.y',其中u为奇数,v可奇可偶,d=gcd(u,v)为奇数;2)已知ext_gcd(u,v)如何求ext_gcd(2u,v)=u'.x' + v'.y',其中v为奇数,u可奇可偶,d=gcd(u,v
阅读全文
摘要:扩展的gcd算法即除了计算gcd(m,n)还要计算整数x和y,使之满足gcd(m,n) = m.x + n.y。 下面的算法中使用迭代方式。 extendedGCD2方法是extendedGCD的简化版本,考虑到在初值向量r{-1} = [1 0], r{0} = [0 1]下,满足递推关系:r{i} = r{i-2} - q{i}.r{i-1}。采用Euclid's算法时,不仅要r(余数)的值,还需要q(商)的值。本例实现参考了Wikipedia中介绍的迭代方法:http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm/**
阅读全文
摘要:二进制GCD算法基本原理是:先用移位的方式对两个数除2,直到两个数不同时为偶数。然后将剩下的偶数(如果有的话)做同样的操作,这样做的原因是如果u和v中u为偶数,v为奇数,则有gcd(u,v)=gcd(u/2,v)。到这时,两个数都是奇数,将两个数相减(因为gcd(u,v) = gcd(u-v,v)),得到的是偶数t,对t也移位直到t为奇数。每次将最大的数用t替换。二进制GCD算法优点是只需用减法和二进制移位运算,不像Euclid's算法需要用除法,这在某些嵌入式系统中可能排上用场。本例实现参考了<<计算机编程的艺术>>(Knuth)第二卷中介绍的算法。/** *
阅读全文
摘要:求两个整数的GCD有两个方法:采用欧几里得算法(Euclid's Algorithm)和二进制GCD算法, 这里实现的是欧几里得算法。欧几里得算法基本原理很简单,即: m = q1.n + r1m2= q2.n2 + r2 ....mi = qi.ni + ri其中m2=n, n2=r1....gcd(m,n) = gcd(m2,n2) = gcd(mi,ni)....直到ri=0(因为0<=ri<ni,所以ri可以收敛到0)。实现:/** * * @author ljs 2011-5-17 * * solve gcd(m,n) using Euclid's Alg
阅读全文
摘要:用Eratosthenes筛子法:import java.util.ArrayList;import java.util.List;/** * * @author ljs * Sieve of Eratosthenes: determine the prime numbers within the range 1....n (n>0) * */public class Sieve { //intput: n is an positive integer (n>0) //output: a list of primes from 1 to n, as 2,3,5.... public
阅读全文
摘要:原问题位于:http://poj.org/problem?id=1015以下为问题描述的摘录:In Frobnia, a far-away country, the verdicts in court trials are determined by a jury consisting of members of the general public. Every time a trial is set to begin, a jury has to be selected, which is done as follows. First, several people are drawn r
阅读全文
摘要:整数的任意拆分问题(不允许重复)问题:输入两个整数n 和m,从数列1,2,3.......n 中随意取几个数(不许重复), 使其和等于m (m<=1+2+...n),要求将其中所有的可能组合列出来.分析:记整数p可以用1,2...q的所有不重复数之和表示的组合为C(p,q),则C(p,q)可以表示为以下的组合:{q} + C(p-q,q-1) (如果q<=m)或者C(p,q-1)即要么包含q,要么不包含q。另外有C(0,K)={}, C(K,0)无解(K>0)因此可以使用动态规划的方法解决。实现:import java.util.ArrayList;import java.u
阅读全文
摘要:中位数(median)是一个排好序的元素中中间位置的元素,如果元素个数为偶数,则是中间两个元素的平均值。例如(3,1,5)的中位数是3,而(2,1,3,5)的中位数是2.5。查找中位数属于Selection Algorithms的一种。用快速排序可以做到每次divide之后,只需要conquer一个分支,时间复杂度为O(nlogn)。为便于比较,以下代码包含quicksort的算法实现: public static float findMedianByQuicksort(int[] A,int left,int right){ //if(right<left) // throw new
阅读全文
摘要:输入一序列的整数(共n个),找出前十个元素(Top 10)。可以用Heap实现:在堆满时,如果要插入一个新的元素,则需比较该元素是不是当前堆中最小的元素,如果不是,则需要将该新元素替换最小的元素,从而维护一个TOP N的堆。通常最小的元素查找需要线性时间,因为只需要查找叶子节点(这是由堆的ordering property决定的),而叶子节点个数最多为2^O([logn] ) = O(n),所以查找最小元的总开销为O(n),然后需要重新构建堆,其总开销为O(nlogn), 二者加起来总的时间复杂度为O(nlogn), 空间开销为O(1)。显然比通过排序查找Top N的空间复杂度要低得多!实现:
阅读全文
摘要:static int inversePairs(int[] A,int left,int right){ if(right<left) return 0 ; int mid = (left + right)/2; int inverseCnt = 0; if(mid>left){ inverseCnt += inversePairs(A,left,mid); } if(mid+1<right){ inverseCnt += inversePairs(A,mid+1,right); } //merge the two parts int i1=left; int i2=mid+
阅读全文
摘要://输入p: 访问概率数组; 0,1,....n是排好序的Key值 //输出A=new float[n][n]: 最优时间矩阵 //输出R=new int[n][n]: 根结点矩阵 static void optimalBST(float[] p,float[][] A,int[][] R){ int n = p.length; for(int i=0;i<n;i++){ A[i][i] = p[i]; R[i][i] = i; //the root is itself } for(int diag=1;diag<n;diag++){ for(int i=0;i<n;i++)
阅读全文
摘要:问题: 输入N个不同字符,如'a','b','c','d','e', 计算从这个字符集合中任意抽取n个(n<=N)不同字符的所有组合结果,比如从中抽取4个字符的组合结果为abcd, abce,abde,acde,bcde共5个。分析:从字符全排列的算法(参见上一篇"字符集的排列算法")中可以知道,组合操作只是选取一部分字符作排列,同时需要把重复的情况剔除,如abc和cba是两个不同排列,但是只算一个组合,去掉重复的情况可以按次序选取元素,即下一个位置(pos)的字符需要大于上一个位置的字符
阅读全文
摘要:问题:输入一串字符,每个字符都是唯一的,比如'a','b','c','d', 需要输出这串字符的全部排列。分析:根据排列原理,排列的可能性有n!种(n为字符的个数)。直觉上讲,首先从字符集合中选取一个字符,然后从剩下的字符中选取另外一个字符,如此下去,直到所有字符用完为止。另外一种可能性是:先假设一个子串已经排列好,比如abc,然后将下一个字符串d插入到已经排好的字符串的两边和中间位置(这里共有4个位置)。下面实现的是第一种算法:实现://chars: 输入的字符串//used: 用于标记已经用过的字符//pos: 当前处理的字符
阅读全文

浙公网安备 33010602011771号