1、2005年浙江省队培训 第3讲 序列和字符串的算法,目录,一、算法分析基础 二、排序 三、序列的算法 四、串的算法,一、算法分析基础,抽象操作,抽象操作(abstract operations). 对于单一操作(如加法)的算法, 运行时间 = 操作时间 * 操作次数 (不考虑cache等体系结构方面的影响) 操作时间取决于计算机 操作次数取决于算法 算法分析: 只考虑算法特性, 因此只考虑操作次数,操作数函数,在很多情况下, 基本操作数可以写成N的函数f(N), 其中N代表主要参数 例如, 给N个数排序的问题, N就是主要参数, 它最明显的决定了问题的复杂程度, 也影响着算法效率 存在多个主参
2、数的情况类似定义, 本节暂不考虑,比较两个算法,假设有两个算法 算法一执行了f(n)=n2次基本操作 算法二执行了g(n)=n2/2次基本操作 那个算法好呢? 绝对操作数算法二好,因为g(n) f(n) 增长情况呢? n扩大10倍,f(n)扩大100倍,g(n)也扩大100倍 两个算法的增长情况一样! 我们说: 渐进时间复杂度一样!,渐进时间复杂度,f(n)=n2和g(n)=n2/2 结论: 增长情况一样 问题: 如何表示“增长情况”? 方法: 把f(n)和g(n)变成“渐进”形式,然后直接比较 如何变成“渐进”形式?只保留最“大”项, 忽略系数, 符合前面介绍的原则 例1:3n4+8n2+n
3、+2 n4 例2:2n+1+n100+5 2n (为什么n+1变成了n?),常见的函数增长,f(N)的渐进形式往往是以下某个函数 1(常数, constant): 和N无关, N多大运行时间都一样 logN(对数, logarithmic): 这是个增长缓慢的函数. 若底为2, 则log1000000约为20 N(线性, linear): 对于必须处理N个输入数据, 或者得到N个输出数据的问题, 算法(在渐进意义下)是最优的. N2(平方, quadratic): 若N加倍, 函数值变为四倍 2N(指数级, exponential): N很小时2N已经很大了 多项式算法Na: 有效算法,函数增
4、长和运行时间,对数函数,若bL=n, 记L = logbn,称L为以b为底的n的对数 对数的公式 logan + logam = loganm klogan = logank 换底公式: logan/logbn=logba 对数是一种增长很慢的函数 log21000 约为 10 log21000000 约为20,对数函数,由于算法分析忽略常数,通常logN不指定底, 默认情况为2, 记为lgN, 若底为自然对数e(=2.71828), 记为lnN lgN为N二进制表示中的位数,lg10=3.322 有时也取对数的对数 loglogN. 由于lglg1012=lg39.865.32, 所以一般可
5、以把它看作常数,其他常见函数和近似,复杂度分析不清楚怎么办,只分析上限,而不需要精确计算实际运行时间 若n充分大时|f(n)|=c|g(n)|,其中c为某常数 记f(n) = O(g(n),表示g(n)为f(n)的渐进上限 例1:5n2+3n+1 = O(n2) 例2:2n3 = O(7n8) 类似的,可以定义下限 如果上下限相等,增长情况完全一样,记做 由于上限有无限多个,我们希望它尽量精确, 否则我们的分析就过于悲观了,实际算法没那么糟糕,递归式,在很多时候, 无法写出时间复杂度T(n)的显式表达式, 而只能得到递归方程 公式一: T(1)=1, T(n)=T(n-1)+n, 则T(n)为
6、n(n+1)/2 公式二: T(1)=1, T(n)=T(n/2)+1, 则T(n)约为lgN 公式三: T(1)=0, T(n)=T(n/2)+n, 则T(n)约为2n 公式四: T(1)=0, T(n)=2T(n/2)+n, 则T(n)约为nlgN 公式五: T(1)=1, T(n)=2T(n/2)+1, 则T(n)约为2n 考虑一般情形: T(n) = aT(n/b) + f(n),递归树分析,T(n) = aT(n/b) + f(n), a, b为常数,f(n)为给定函数 递归树: T(n) = f(n)+af(n/b)+a2f(n/b2)+aLf(n/bL) 其中最后一项为递归边界,
7、即n/bL=1,因此L=logbn,公式四分析,T(n) = aT(n/b) + f(n) 递归树得到的结果: T(n) = f(n)+af(n/b)+a2f(n/b2)+aLf(n/bL) 其中L=logbn 公式四:T(n) = 2T(n/2) + n a = 2, b = 2, f(n) = n 对于第k项,有2kf(n/2k) = 2k *n/2k = n 一共有log2n项 T(n) = n * log2n = O(nlogn),最大连续序列问题,给一串整数a1n,求出它和最大的子序列,即找出1=i=j=n,使ai+ai+1+aj最大 介绍四个算法并分析时间复杂度 枚举:O(n3)
8、优化的枚举:O(n2) 分治:O(nlogn) 贪心:O(n),算法一,max := a1; for i:=1 to n dofor j:=i to n do beginsum := 0;for k:=i to j dosum := sum + ak;if sum max then max := sum;end; 思想:枚举所有的i和j,计算ai+aj,选择最大的 时间复杂度如何分析? 三层循环 内层操作次数取决于i, j 中层操作次数取决于i,算法一分析,当i和j一定的时候,内层循环执行j-i+1次 总次数为 应该如何计算? 方法一:直接计算 先计算里层求和号,得 再加起来?好复杂 直接算法
9、需要利用公式12+n2=O(n3) 一般地,有1k+nk=O(nk+1). 证明: 数学归纳法,算法一分析(续),总次数为: 完全的计算太麻烦 能不能不动笔就知道渐进时间复杂度呢? 何必非要计算出详细的公式再化简呢? 里层求和计算出的结果是O(n-i)2) 12+22+n2=O(n3) 每步都化简!但是要保留外层需要的变量 结论:算法一时间复杂度为O(n3) 经验:一般只能支持 n=200,算法二,思路 枚举i和j后,能否快速算出ai+aj呢? 预处理! 记si = a1 + a2 + + ai, 规定s0 = 0 则可以在O(n)的时间内计算出s1, , sn s0 := 0; for i:
10、=1 to n dosi := si1 + ai;,算法二(续),有了si,怎么快速求ai+aj呢? ai+aj = (a1 + + aj) (a1 + + ai-1) =sj si-1 而si经过预处理以后可以直接读出!max := a1; for i:=1 to n dofor j:=i to n doif sj si-1 max then max := sj si-1;时间复杂度:预处理+主程序=O(n+n2)=O(n2). n=5000,算法三,用一种完全不同的思路 最大子序列的位置有三种可能 完全处于序列的左半,即j=n/2 跨越序列中间,即in/2j 用递归的思路解决! 设max(
11、i, j)为序列aij的最大子序列, 那么,算法三(续),用递归的思路解决! 设max(i, j)为序列aij的最大子序列 设mid = (i + j)/2,即区间aij的中点 最大的第一种序列为max(i, mid) 最大的第二种序列为max(mid+1, j) 问题:最大的第三种序列为? 既然跨越中点,把序列aij划分为两部分 aimid:最大序列用扫描法在n/2时间内找到 一共只有mid-1=n/2种可能的序列,一一比较即可 amid+1j:同理 一共花费时间为j-i+1,算法三分析,如何分析时间复杂度呢? 我们没有直接得到具体的T(n)的式子 但是容易得到递推关系T(n) = 2T(n
12、/2) + n 设时间复杂度的精确式子是T(n) 第一、二种序列的计算时间是T(n/2),因为序列长度缩小了一半 第三种序列的计算时间是n 由递归式四, 得T(n)=O(nlogn),算法四,算法二的实质是求出i max then max := sj min_s; end; 时间复杂度很明显:O(n). n = 1,000,000,总结,二、排序,问题1: 排序,给n个数, 从小到大排序,思考: 篮球俱乐部,给定N个人的身高(1.95m2.05m),要求将他们排成一列,使得任意选取两个人,他们中间如果存在K个人,并且身高和为S,那么S与K2m的差的绝对值小于等于0.1m。,插入排序和选择排序,
13、增量算法选择排序?取决于未来的输入?插入排序:来一个插一个 部分排序插入排序?如果前k大的是最后k个元素选择排序:选的前k个就是前k大元素 结论:排序算法的选择要看实际应用,归并排序,二路归并 1, 2, 4, 7, 9 3, 5, 6, 10, 11 合并:n 分治sort(i, j),设时间为T(n)排前一半:sort(i, mid), 时间T(n/2)排后一半:sort(mid+1, j),时间T(n/2)合并起来:n,归并排序,算法分析 时间: T(n) = 2T(n/2) + n T(n) = O(nlogn) 空间: S(n) = 2S(n/2) + n? 空间可重用! 最好、最坏
14、、平均是一致的,例题: 煎饼,目标:从小到大排序 从下数第k个开始往上翻 (a) (b) (c)3 1,问题2: 逆序对数目,逆序对数目:i aj 枚举:O(n2). n = mid,递归处理 i mid j,合并的时候顺便求!1, 2, 4, 7, 93, 5, 6, 10, 11 取后一半元素时,前一半还剩多少个就是 时间复杂度:O(nlogn),快速排序,找一个数x让x左边的数都比x小让x右边的数都比x大分别给两边排序核心:如何调整x左右的数? 从两边往中间扫描在x左边第一个比x大的地方停下来在x右边第一个比x小的地方停下来 两个交换,正好都满足要求,快速排序,例子:1, 8, 2, 9
15、, 6, 4, 7, 3, 5 第一次交换8和5:1, 5, 2, 9, 6, 4, 7, 3, 8 第二次交换9和3:1, 5, 2, 3, 6, 4, 7, 9, 8 第三次交换6和4:1, 5, 2, 3, 4, 6, 7, 9, 8 两个指针汇合,完成 时间复杂度:O(n),快速排序分析,最好情况:均分成两半,则是O(nlogn) 最坏情况:分成长度为1和n-1,则是O(n2) 这是不是说明快速排序比归并排序差 显然不是,否则就不会叫“快速排序”了嘛 加入随机数, 几乎可以保证是O(nlogn), 系数比归并排序小 随机数让坏情况从数据转移到了随机运气,快速排序,一些小细节 n = 1
16、0时用插入排序加速 x如何选?中间?随机(随机数产生开销)? 警告!快速排序不稳定!原因?如何修改? 最坏情况:数据随机数,问题3: 求序列中第k大数,方法一: 先排序O(nlogn) 方法二: 借用快速排序的框架 只需要根据k的大小只处理一边 平均情况:O(n),例题: 士兵排队问题,n=10,000个士兵在网格中 位置用(x, y)表示士兵可以沿四个方向移动进入某一行且排在一起假设格子可以容纳所有士兵,分析,行:感觉在”中间” 中位数 or 平均数? 假设往下一行 列?(请思考),思考: 加权中位数,X轴上有n个点,第i个点都的权值为正数Vi,要求在X轴上找出一点P,使sumVi*|xp-
17、xi|最小,计数排序,特殊的排序对象:0100的整数(如分数) 开一个count0100的数组,记录个数for i:=1 to n doinc(countscorei); 时间复杂度为O(n), 比快速排序还快 关键: 利用了关键码的范围,基于比较的排序时间下限,简单起见,不考虑相等的情形 可以获得多少信息?一次比较,两种结果k次比较2k种结果 需要获得多少信息?n个数的排列有n!种最后只剩一种可能性时,排序完成,基于比较的排序时间下限,需要比较多少次?比较k次以后最好只能保证剩n!/2k种可能性n!/2k=1时,即k=log(n!)时排序完成 log(n!) = (nlogn),三、序列的算
18、法,问题1: RMQ问题,给n个数a1n 设计在线算法, 对于每个询问(L, R), 回答aLR内的最小值,分析,算法一: O(n2)-O(1) RMQi,j = minRMQi,j-1, Aj 用长度递增的顺序计算所有RMQi,j 算法二: O(n)-O(n1/2) 分为长度L=n1/2的小块, 共L块 预处理得到每个块的最小值 询问最多涉及L个块和头尾块的2L个元素 好处: 修改也是O(n1/2)的,分析,算法三: O(nlogn)-O(1) di,j为从i开始的, 长度为2j的范围内的RMQ 递推式di,j=mindi,j-1,di+2j-1,j-1. 预处理是O(nlogn)的 询问时
19、取k=log2(j-i+1), A为从i开始长度为2k的块, B为以j结束的长度为2k的块, 则A和B覆盖区间(i,j). 询问是O(1)的 算法四: O(n)-O(1) 用Cartesian-Tree转化成LCA问题,问题2: k路归并问题,把有k个有序表, 合并成一个有序表. 元素共有n个.,分析,每个表的元素都是从左到右移入新表 把每个表的当前元素放入堆中, 每次删除最小值并放入新表中, 然后加入此序列的下一个元素 每次操作需要logk时间, 因此总共需要nlogk的时间,问题3: 序列和的前n小元素,给出两个长度为n的有序表A和B, 在A和B中各任取一个, 可以得到n2个和. 求这些和
20、最小的n个,分析,可以把这些和看成n个有序表: A1+B1 = A1+B2 = A1+B3 = A2+B1 = A2+B2 = A2+B3 = An+B1 = An+B2 = An+B3 = 类似刚才的算法, 每次O(logn), 共取n次最小元素,共O(nlogn),问题4: 多个有序表的第k大元素,有m个有序表, 每个表里有n个元素. 所有元素不超过W 设计在线算法, 回答这m*n个元素中第k个元素的值,分析,二分元素大小x 计算每个有序表里比x小的元素个数k, 每个表O(logn), 共O(mlogn) 如果k=k, 输出x 如果kk, 把x变小 总时间复杂度O(mlogn*logW),
21、问题5: 范围内第k大数,给n个数A1n 设计在线算法, 对于询问(i, j, k), 返回Aij第k大元素,分析,预处理:建立线段树,每个线段保存该区间内元素排序好的序列 查询处理:任意i,j可分解为最多2logn个不重叠区间的并,只需二分W, 每次计算2logn个区间内一共有多少个数比W大,用logW次时间可求出第k大元素,分析,区间内的数已排序,用二分每个区间求比W大的数logn 累加所有2logn个区间比W大的数,共log2n 实现: 一个归并排序可以同时构造线段树和每个节点内的排序数组. 时间: O(logW*log2n) 空间:O(nlogn),例题: 区间,有n个闭区间ai,bi
22、,其中i=1,2,n,每一段被涂成黑色 整个数轴被涂为几段黑色?,分析,按左端点从左到右排序 从左到右扫描,若下一个区间的左端点分离,则区间数加一,否则扩展当前区间的右端点 时间复杂度:O(nlogn),例题: 轮廓线,每一个建筑物用一个三元组表示(L, H, R), 表示左边界, 高度和右边界 轮廓线用X, Y, X, Y这样的交替式表示 右图的轮廓线为: (1, 11, 3, 13, 9, 0, 12, 7, 16, 3, 19, 18, 22, 3, 23, 13, 29, 0) 给N个建筑,求轮廓线,分析,算法一: 用数组记录每一个元线段的高度 离散化, 有n个元线段 每次插入可能影响
23、n个元线段, O(n), 共O(n2) 从左到右扫描元线段高度, 得轮廓线 算法二:每个建筑的左右边界为事件点 把事件点排序, 从左到右扫描 维护建筑物集合, 事件点为线段的插入删除 需要求最高建筑物, 用堆, 共O(nlogn),思考: 照亮的山景,在一片山的上空,高度为T处有N个处于不同水平位置的灯泡,如果山的边界上某一点与某灯i的连线不经过山上的其他点,我们称灯i可以照亮该点。开尽量少的灯,使得整个山景都被照亮。灯的位置固定。一定有解,思考: 喷水装置,有一块草坪,长为l,宽为w 在它的中心线上装有n个点状的喷水装置 效果是让以它为中心半径为ri的圆被润湿 选择尽量少的喷水装置把整个草坪
24、全部润湿,,例题: 啤酒厂,n个点连成环状,每相邻两点间有距离值 如果选择其中一个点建立啤酒厂,则它需要给所有其他城市运送啤酒 每个城市都有啤酒需求量di 运到城市i的费用为啤酒厂到i的最小距离乘以di 选择让总费用最小的啤酒厂,分析,如果啤酒厂建在s,则所有城市分为两部分 s顺时针方向移动时p也顺时针方向移动,处理p移动的时间为O(n) 总费用的改变量来源于四部分:始终S-的部分,始终为S+的部分,从S-变为S+的部分,从S+变为S-的部分,例题: 水库,已知有N个水库,水库的当前水量为Wi,能够储存的容量为Li。现在可以选择炸掉其中的某些水库:一旦炸掉第i个水库,那么水库中的水就会流到第i
25、+1个水库。如果某个水库的当前水量超过了容量,那么这个水库将回倒塌,而所有当前的水也会流到下一个水库。炸第i个水库需要的费用为Ci,现在的目标是使第N个水库倒塌或者爆炸。,分析,设用炸弹炸的第一个水库为第i个水库。则显然第1i-1个水库都安然无恙,第i+1n个水库要不被炸掉要不被水冲倒塌。(否则费用不会最小) 第j个水库是否要需要炸掉?这只要看Wi+Wj是否大于Lj即可:设Si=W1+Wi,则Sj-LjSi-1时不需要用炸弹,否则需要用炸弹。,分析,若我们从大到小的枚举i,则Si-1是递减的,也就是说当i=k水库j不需要用炸弹时,iSi-1的水库的从堆中删去。,例题: 序列反转,N个球按照编号
26、从1到N排放在桌上。定义一种操作,每次将xy范围内的球,进行倒置,就是把它们反过来放。如2 3 7 4变成了4 7 3 2。给定M个连续的操作,要求我们给出操作后的序列。,算法一,离散化加模拟。初始时候,只有1N一个块,每次处理一个操作,会将一个块分成最多三块,块的顺序适当改变,另外每个块自身可能被翻转 因为我们基于“块”进行操作,类似于离散化,每次最多增加2块,因此总时间复杂度为O(M2)。,算法二,首先把这n个数分成sqrt(n)段, 每段长度基本相等. 处理时只要处理首尾两段就可以了, 中间的段只要记录当前是顺序还是逆序 时间复杂度O(n0.5m),四、串的算法,字符串,字符串: 可变长
27、的字符数组. 本文中串S的第i个字符用Si表示, 从第i个到第j个字符用Sij表示 实现: 可以在末尾加一个结束标志, 如0, 也可以记录字符串的长度 基本操作: 取长度, 取指定位置的字符, 取子串, 模式匹配,问题1: 替换连续空格,问题: 把连续空格改为单个空格. 例如对输入s=There are many spaces., 输出为There are many spaces.,分析,算法一: 每遇到空格就把后面的字符往前移 算法二: 用两个指针, 读指针持续移动, 当遇到第二个空格时写指针不动. 比较: 算法一最坏O(N2)的, 算法二是O(N)的 启发: 字符串问题一般容易设计正确的算
28、法, 但需要深入分析才能找到高效算法,问题2: 字符串排序,给n个字符串 把它们按字典序从小到大排序,分析,和数值排序类似, 基本操作是字符串比较 由于字符串可能比较长,一般不移动字符串本身, 只修改指针,问题3: 模式匹配,给定串T,寻找串P是否在T中出现 T称为母串,P称为模板。一般记T和P的长度分别为n和m 输入T和P,Ti为T从第i个字符开始的后缀 输出 判定问题:P是否在T中出现 完整匹配:P出现的位置集合 前缀匹配:对于1=i=n,P与Ti的最长公共前缀长度记为Li,输出数组Li,经典模式匹配算法,T称为母串,P称为模板。一般记T和P的长度分别为n和m 依次判断T从第i个字符开始的
29、长度为m的串是否和P相同 判断字符串相同: O(n) 枚举起点: O(n) 最坏情况: O(n2),经典的模式匹配,多余的比较,经典的模式匹配算法有很多多余比较由于ABRA已经匹配到了, 肯定下一个起始位置是匹配不到的, 不管母串如何! 结论: 母串指针可以保持不回溯! (回溯匹配的成功与否和母串无关, 只由模板决定) 换句话说, 模板P自身的性质决定了: 应该直接把起始位置移动3位而不是一位!,有穷自动机,模板P自身的性质可以用有穷自动机来描述, 用它保证T指针不回溯 有穷自动机有一个起点”$”和终点”!” 字符比较成功, 沿粗边走(T, P指针同时右移一格) 失败, 沿细边走(T指针不动,
30、 P指针回移) 细有向弧ab表示: 如果比较字符a时失败, 则转向比较字符b(因为它前面的字符串肯定是相同的),有穷自动机,最后1个字符指向第4个字符, 表示如果比较最后一位时出错,P指针移回第4个字符 验证: 匹配到最后一个字符时出错,它前面三个字符肯定是ABR, 因此可以直接看第4个字符,KMP算法,预处理模板. 用fail1n表示自动机. faili表示比较第i个字符失败时P指针的新位置 主算法如下(i是T指针, j是P指针),分析,时间复杂度分析 情况一: 比较成功, i和j都加1 情况二: 比较失败, i不变, j减小 由于i最多增加n-1次, 因此j最多增加n-1次(j增加1时i也
31、增加1). 情况一O(n) j减少的次数不会超过j增加的总量, 因此j减少最多n-1次. 情况二O(n) 总O(n)+O(预处理). 关键: 如何算fail?,失配函数的计算,KMP算法难理解的地方在于fail函数的计算! 回忆fail的意义: 比较失败时P指针的新位置 不要指向一个肯定不会产生匹配的地方 不要漏掉可能会产生匹配的地方 换句话说, fail应该指向最后一个可能产生匹配的地方(回溯得尽量少, 或者说fail值尽量大). 用数学语言描述,Fail的含义,换句话说, fail应该指向第一个可能产生匹配的地方. 用数学语言描述 P1failj-1是P1j-1的最长真前缀, 它同时也是T
32、1i-1的后缀 由于已经匹配了前j-1个字符, 所以P1j-1是T1i-1的后缀, 因此 P1failj-1是P1j-1的最长真前缀, 它同时也是P1j-1的后缀,Fail举例,以下是一个举例, 可以对比 P1failj-1是P1j-1的最长真前缀, 它同时也是P1j-1的后缀朴素算法: 对于给定的j 从大到小枚举failj, 直到P1failj-1是P1j-1的后缀 检查后缀需要O(m)的时间, 枚举failj需要O(m), 枚举j需要O(m), 一共O(m) KMP的过人之处(也是让人迷惑之处)在于: 它用模板自己匹配自己, 在O(m)时间内求出了所有fail,Fail计算算法,它依次求出
33、fail1, fail2failm, 利用到了以前求出的P 实质是用P自己匹配自己, i是T指针, j是P指针,正确性证明,用数学归纳法. fail1是显然的 假设在(*)行算出的faili是正确的, 下面证明faili+1也是正确的 刚执行(*)计算出faili时j = faili, 因此P1j-1是P1i-1的最长真前缀也是后缀 定义迭代fail如下(注意fail0无意义),正确性证明,用数学归纳法容易证明: 所有形如P1failcj-1同时是P1i-1的前缀和后缀 因此最长的P1i-1的前缀/后缀也行如P1failcj-1, 其中c是满足Pfailcj=Pi的最小c(这就是里层循环体做的
34、事情). 计算faili的过程可以看成是动态规划, 不断用当前c的fail值进行更新,Fail的优化,如果Pj和Pfailj是同一个字符, 那么马上又失败! 在计算时判断一下即可, 或做后处理(post-processing),优化效果,时间复杂度没有降低, 但通常把系数缩小一半!,例题: 反片语,如果两个单词可以交换字母顺序相互转换, 为片语(anagram) 如果某单词不和任何其他单词组成片语, 则为反片语(ananagram) 找出所有反片语,分析,判断片语为O(LlogL), 先排序 算法一: 判断每两字符串是否互为anagram, 最坏情况O(n2)次比较 算法二: 给每个串的字母排
35、序 给所有串排序 检查相邻串是否相同 只需要O(nlogn)次比较,例题: 破解编码,给一个1k的排列, 每k个一组做排列进行加密. 例如排列2431, 则Maryan被变换成yMra?a?n 给原文和密文, 把一段新信息加密. 如果多个排列满足条件, 选k最小的. 不会出现两个最短排列都满足. 信息长度n不一定是k的倍数,分析,从小到大枚举k, 然后得到排列. 注意排列可能有很多(因为字母有重复) 对于每个组, 可以猜测得到一个排列, 例如Mary-yMra则只有可能是2431. 但如果是abab-aabb, 有可能是1324, 1342等. 但如果又有cdcd-ddcc, 则无解了. 排列
36、的第i个数的取值集合为所有集合的交 知道了第i个位置可以取哪些数, 求匹配 图是特殊的!,例题: 关键词,给出一个长度为n的01串A,求一个最短的由01串B,使B不是A的子串。,分析,m=logn取上整,由抽屉原理知B长度=m 可以把长度不超过m的子串一一对应于一棵深度为m的完全二叉树T的节点。 根节点编号为1对应空串S1,i的左儿子编号为2i,对应Si+0,i的右儿子编号为2i+1,对应Si+1 用Marki表示节点i对应的子串是否在串A中出现过。我们的任务是求最小的i使得Marki=false。,分析,用O(n)的方法可以把T的叶子结点的Marki都计算出来(每次左移一位并加新值) 把An
37、-m+1,n,An-m+2,nAn,n所对应的值也标记为true(每次右移一位) 从大到小的循环i 如果Marki=false则更新最小值为I 否则Marki shr 1=true,例题: 重复,有一个n个长度不超过L的单词,单词中的字母来自字母表a,z 。编程求所有单词中最长的公共子串(必须在原串中连续),分析,枚举最短串子串再用KMP:O(nL3) 枚举所求串在最短子串中的开始位置,取其他串KMP结果(最远能匹配到的位置)的最小值为结束位置,O(nL2) 或者枚举开始i和结束j,但成功时j+,不成功时i+,思考: 窗口属性,对于所有K,每连续K个字符组成的串最多有K+1种, 如:011010 给一个串, 求最小的s, 使得前s个字符有WP, 前s+1个没有,