后缀数组构建–倍增算法java实现
2011-12-27 20:08
295 查看
当把倍增算法的流程图画出来之后,就可以理解算法的流程。按照这个流程实现,我开始采用treemap来进行排序,10^5的长度的字符串,耗时10秒左右。今天按照某Acmer的代码,实现了java版的。性能提升接近100倍。Acmer写的代码,一般效率很高,但是可读行非常差,阅读代码的人经常绕在里面出不来了。下面,我详细分析一下java版的实现,希望自己加深理解,也能够帮助同学,欢迎指教。
代码解释:整体思想,参考前面的博客《后缀数组构建-倍增算法分析》。函数接收三个参数:
待排序的数组:这里有两点注意,数组元素类型是整形的,要先将字符转化为整形,方法采用转ASCII码的方法即可;还有就是,数组的长度是实际待排元素的两倍,也就是2*n,为什么后面会讲到。
待排元素的长度,就是待排数组的前n个元素,才是真的参与排序的。
计数排序数组的大小,为待排数组中元素最大的+1
10行初始化计数数组,元素默认为0(在C++中,没有默认值,需要初始化)。11行初始化后缀数组(排第几是谁?切记),元素默认为0。 12-18行是使用计数排序的方法,得到后缀数组sa(这个sa可以理解为,排第x的是y,y又是arr[z],可以说成排第x的是z,这种绕弯儿的地方挺多的,注意理解)。
倍增算法,采用基数排序的一个好处是,可以利用上次的做第一次排序(一共两次)。19行的r1数组,就是存储第一次排序结果的。20行的order数组,是存储临时rank数组,或者最终rank数组的。
进入for循环,j表示子串长度(其实是子串长度除以2),p作为循环结束的条件,是表示的每次循环结束后,得到的rank数组中的最大的值,如果是n个元素的数组,则最后p的值为n,结束循环。之后的每次循环,子串长度扩大一倍,将max的值置为p(调整count数组)。
接着22-26行是两个循环,求出r1数组,即第一次排序的结果。这一步不好理解,我们以“aabaaaab”的求解过程为例,进行解释:
子串长度为1的时候,rank数组为[1,1,2,1,1,1,1,2],suffix数组为[0,1,3,,4,5,6,2,7],接着处理子串长度为2的情况:aa,ab,ba,aa,aa,aa,ab,b$($表示空)。suffix数组为[7,0,2,3,4,5,1,6]。通过观察,我们发现,7=8-1,0=1-1,2=3-1等等。就找到了规律,再试几组,会得到,后面的减1,就是减j。27-35行是采用计数排序的第二次基数排序,求得后缀数组。36、37行是由后缀数组得到rank数组,这里最需要注意的是:有的子串是相同的,需要判断,如果相同的,rank值也是相同的。
后面的代码比较好理解,不多解释。阅读Acmer的代码的心得:代码效率确实高,但是可读性奇差无比。这个在实际项目中,需要注意,要兼顾效率和代码的可读性。
这几天学习算法的一个重要的心得:多在纸上画画过程。
【引用】
代码解释:整体思想,参考前面的博客《后缀数组构建-倍增算法分析》。函数接收三个参数:
待排序的数组:这里有两点注意,数组元素类型是整形的,要先将字符转化为整形,方法采用转ASCII码的方法即可;还有就是,数组的长度是实际待排元素的两倍,也就是2*n,为什么后面会讲到。
待排元素的长度,就是待排数组的前n个元素,才是真的参与排序的。
计数排序数组的大小,为待排数组中元素最大的+1
10行初始化计数数组,元素默认为0(在C++中,没有默认值,需要初始化)。11行初始化后缀数组(排第几是谁?切记),元素默认为0。 12-18行是使用计数排序的方法,得到后缀数组sa(这个sa可以理解为,排第x的是y,y又是arr[z],可以说成排第x的是z,这种绕弯儿的地方挺多的,注意理解)。
倍增算法,采用基数排序的一个好处是,可以利用上次的做第一次排序(一共两次)。19行的r1数组,就是存储第一次排序结果的。20行的order数组,是存储临时rank数组,或者最终rank数组的。
进入for循环,j表示子串长度(其实是子串长度除以2),p作为循环结束的条件,是表示的每次循环结束后,得到的rank数组中的最大的值,如果是n个元素的数组,则最后p的值为n,结束循环。之后的每次循环,子串长度扩大一倍,将max的值置为p(调整count数组)。
接着22-26行是两个循环,求出r1数组,即第一次排序的结果。这一步不好理解,我们以“aabaaaab”的求解过程为例,进行解释:
子串长度为1的时候,rank数组为[1,1,2,1,1,1,1,2],suffix数组为[0,1,3,,4,5,6,2,7],接着处理子串长度为2的情况:aa,ab,ba,aa,aa,aa,ab,b$($表示空)。suffix数组为[7,0,2,3,4,5,1,6]。通过观察,我们发现,7=8-1,0=1-1,2=3-1等等。就找到了规律,再试几组,会得到,后面的减1,就是减j。27-35行是采用计数排序的第二次基数排序,求得后缀数组。36、37行是由后缀数组得到rank数组,这里最需要注意的是:有的子串是相同的,需要判断,如果相同的,rank值也是相同的。
后面的代码比较好理解,不多解释。阅读Acmer的代码的心得:代码效率确实高,但是可读性奇差无比。这个在实际项目中,需要注意,要兼顾效率和代码的可读性。
这几天学习算法的一个重要的心得:多在纸上画画过程。
【引用】
IOI国家集训队2009年论文《后缀数组——处理字符串的有工具》(罗穗骞)。
相关文章推荐
- 剑指offer1面试题52 构建乘积数组(java实现)
- java 实现后缀数组及最长回文子串问题
- Java实现构建乘积数组
- 构建乘积数组(Java实现)
- 【Java实现】剑指offer52--构建乘积数组
- 剑指Offer面试题52:构建乘积数组 Java实现
- 数据结构与算法分析笔记与总结(java实现)--数组7:判断单链表是否相交问题构建乘积数组
- java 数组实现哈希表的构建,查找,插入,删除
- 构建乘积数组java实现
- 【Java】一个数组实现三个栈 (未完待续)
- java按钮控件数组实现计算器界面示例分享
- leetcode+华为笔试题-java实现返回一个整数数组中最大子数组的和
- KMP_next数组_while详解_Java实现
- Java语言实现数组的键盘输入(综合几种方法实现)
- java实现 中缀转后缀
- java(数组实现)线性表中顺序表
- 用Ant实现Java项目的自动构建和部署
- 数字在排序数组中出现的次数java实现
- java数组实现N皇后问题
- [置顶] java使用数组实现简单的Map