关于字符串的算法有RabinKarp,KMP,前缀树(字典树),后缀数组,后缀自动机。文章只介绍RabinKarp,KMP,在简单介绍前缀数组,其他两个还没有学到。
RabinKarp算法
算法原理
利用hash--滚动hash
原字符串:[0][1][2][3][4][5][6][7]
匹配字符串:[c0][c1][c2]
匹配字符串长度为n
k为给定进制
利用hash意思就是算出匹配字符串的hash值:hash=c0*k^(n-1)+c1*k^(1)+c2*k^(0)
再将原字符串中每n个的hash值按照上面的方法算出来得到hash数组,hash数组与匹配字符串的hash进行比较,匹配成功hash数组的下标就是在原字符串的下标。,下图便于理解hash下标的对应。
滚动hash是使得hash值算的更快:已知原字符串下标为(0,1,2)的hash值,求下标为(1,2,3)的hash值:hash=hash*k-c0*k^n+c3,往后的下标同理,这样只进行加减会使得速度稍快些
算法冲突
由于按照的是某一个给定的k进制,那么难免会遇到不同字符串算出相同的hash,解决办法就是遇到相同的hash值在对子字符串进行比较
KMP算法
算法原理
引入
我们在进行暴力匹配的时候我们会一个一个进行比较如下图
在比较中我们会发现当有一个元素失配时,匹配字符串中前缀和原字符串失配元素前有n个相同的元素,既然这样我们就不需要比较那n个相同的元素,如下图
我们会发现当元素a与c失配时,失配元素a的后缀b和匹配字符串的前缀b相同,那么他们就不需要在进行比较,就直接比较下标为1的位置,那么就会出现一个函数关系f(L)=K,当L位置失配时我们会去比较K位置。
nest数组
我们有了前面的关系式我们就思考,如果第三个位置我们失配了那原数组后缀和匹配数组前缀是否有相同呢,我们又要跳到哪里才能避免重复检测呢,那第四个位置失配第五个位置失配我们又要跳到那里去才能避免重复匹配呢?随着问题升入我们就会想到一个数组,我们称它为nest数组,里面存入要跳入到哪里的值,代码:nest[ j ]=k; 当j位置失配我们要跳到哪儿去呢,跳到k去。
求nest数组
求nest数组我们你只需要匹配数组就可以了,bababb,当第一个失配我们还是匹配第一个,为了规范我们计为-1,所以nest[ 0 ]=-1;当第二个失配时我们肯定去比较第一个,这里我们就计为0,所以nest[ 1 ]=0;,可见nest数组前两个要跳入的值都是固定的,下面我们手算bababb字符串的nest数组如图
下标为0,1是固定的,当下标为2失配时只需要看2下标之前的,b和a不相同,那么nest[ 2 ]=0;当下标为3失配时,只需要看下标为3以前的元素,已经知道nest[ 2 ]=0;我们只需要查下标2和0对应的元素是否相同,相同则说明在原数组中失配元素的后缀后和匹配字符串的前缀相同的,那么就不需要检测这相同的部分,那么nest[ 3 ]=1;以此类推。
总结一下就是要算出第3个元素失配跳到哪里只需要看nest[ 2 ]=0中下标2,0对应的元素是否相同,若相同就nest[ j+1 ]=k+1;若不相同则nest[ j+1 ]=k;
nest数组的应用
①肯定是字符串的匹配
②求字符串中的重复字符串,nest[ j ]=k;在数组长度的最后多一位失配,若j/(j-k)==0就有重复元素。
后缀数组
什么是后缀数组
就是所有后缀字串的字典序的排序后,排名和原下标的映射,数组只记录排序后的下标
字符串ABAABA的后缀
ABAABA~~0 对这些后缀进行排序后保存他们的下标的数组就是suffixarray
BAABA~~1 简称sa
AABA~~2 sa[ 0 ]=5 意思就是下标为0开始的字符串下标排序后排第5
ABA~~3
BA~~4 还有rank数组
A~~5 rk[ 5 ]=0 意思就是给定后缀的下标,返回它字典序的排名
rk[sa[ i ]]=i;
后缀数组的匹配
规则就是一定有后缀字符串的开头一定是匹配字符串的前缀
我们就用二分法去查
当原数组特别长的时候就会比KMP有优势
求后缀数组
倍增法
总结
RabinKarp相当与动了原字符串和匹配字符串
KMP是动了匹配字符串
后缀数组只动了原字符串
文章欠缺求后缀数组的方法的解释和每一个算法的代码,后续刷题整明朗了在继续补充