字符串排序
键索引记数法
将String[]分成R组,统计每组字符串个数,由此计算出每组的首个字符串在排序后的位置后,即可得到每个字符串排序后的下标。
该方法有一个特点:排序后,同一组内的字符串排序前的相对位置不变,正是这一点使得其可以用于低位优先字符串排序
低位优先的字符串排序
前提条件:每个字符串等长
所谓低位优先,就是从右往左排序,先排最右边的
假设字符串长度W,从右往左,根据字符串当前位置的字符作为组号分成R组,使用键索引记数法完成W次排序即可得到这样有序的数组:
优先比较最左边元素,最左边元素相同,比较后面的元素
1 | public class LSD { |
高位优先的字符串排序
这种排序适合排序不同长度的字符串
首先从每个字符串最左边的第一个字符作为分组标志,使用键索引记数法处理完成排序,之后针对排序后的每一个分组,对后面的字符 递归进行一样的操作
类似快排的思想,但是这里的递归不是把数组分成两部分递归,而是R部分递归
1 | package Number_5; |
三向字符串快速排序
高位优先的字符串排序时,每次根据字符串当前字符的不同,分成R组,而三向排序只分为三组。大于指定字符的,等于指定字符的,小于指定字符的。
只有等于指定字符的,继续向后递归。
不等于指定字符的,继续在当前字符位置递归。
适合于String[]中重复字符串密集的情况
1 | /** |
字符串查找
单词查找树
1、树的每个节点维护:
node[],node数组的下标索引代表单词的一个字符
一个泛型变量val,当前节点表示的字符是单词末尾字符时非空
2、单词查找树的root节点不存在val
无论是增加单词还是查找单词还是匹配单词,从根节点出发,不断查找非空next[i]或创建next[i]
1 | public class TrieST<Value> { |
三向单词查找树
单词查找树是将节点维护的node数组下标隐式代表字符,组成树
三向单词查找树,每个节点不维护包含所有字符下标的节点数组,只维护三个节点:left,mid,right
同时包含一个char表示当前节点代表的字符,如果当前字符等于char,访问mid,小于char,访问left,大于char,访问right
包含一个Value val,单词最后一个字符所代表节点的val非空,表示一个单词的完结
三向的效果不需要维护所有字符个数的数组,只维护三个子节点,添加、查找单词都和当前节点的char比较来决定子节点的选择,而不是单词查找树通过当前字符作为索引查找节点数组。
1 | public class TST<Value> { |
子字符串查找
KMP
KMP的核心思想是要构造next数组。
next[i]的含义
target的第i个字符及其之前的字符组成的子字符串与从target头部开始匹配的字符个数
next数组的求法
初始化:next[0]=next[1]=0next数组在字符串查找中,扮演了极其重要的角色。
- 当target[i]和source[j]不匹配时,不需要完全回退到target[0]重新开始比较,通过查找next数组,可以知道source[j-1]处已经匹配了多少个target字符,所以source[j]可以从其后的target字符开始比较,无需回退source,大大减少了重复比较的次数。
不用KMP的暴力比较,其时间复杂度是
O(source.length() * target.length())
KMP的时间复杂度是
O(source.length() + target.length())
此图帮助你理解KMP的思想:
1 | private int[] next; |
BM
pat按照从右往左与txt比较
当出现对应位不匹配时,使txt[i]与pat中与其相同的字符对齐
1、如果不存在或者pat与其相同的字符在pat当前比较字符的右边,则将pat往后移动一位
2、如果在左边,移动pat使之与txt[i]对齐
继续从右往左比较
如果比完了所有的pat,则找到匹配
如果pat移出了txt的右边界,则匹配失败
1 | public class BoyerMoore { |
RK
将pat转化为一个R进制的数,用除留余数法除以一个较大的素数Q取余得到pat的hash值
计算txt的前M(pat的长度)个字符的hash值
1、如果相等,认为匹配成功(当Q极大时,hash相等但不同字符串的概率极低)
2、如果不相等,减去最左边的字符代表的值(注意是R进制)加上下一个字符的值,再求hash值,重复之前的步骤
1 | public class RabinKarp { |