后缀数组构建–倍增算法java实现

当把倍增算法的流程图画出来之后,就可以理解算法的流程。按照这个流程实现,我开始采用treemap来进行排序,10^5的长度的字符串,耗时10秒左右。今天按照某Acmer的代码,实现了java版的。性能提升接近100倍。Acmer写的代码,一般效率很高,但是可读行非常差,阅读代码的人经常绕在里面出不来了。下面,我详细分析一下java版的实现,希望自己加深理解,也能够帮助同学,欢迎指教。 代码解释:整体思想,参考前面的博客 《后缀数组构建-倍增算法分析》。函数接收三个参数:
  1. 待排序的数组:这里有两点注意,数组元素类型是整形的,要先将字符转化为整形,方法采用转ASCII码的方法即可;还有就是,数组的长度是实际待排元素的两倍,也就是2*n,为什么后面会讲到。
  2. 待排元素的长度,就是待排数组的前n个元素,才是真的参与排序的。
  3. 计数排序数组的大小,为待排数组中元素最大的+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年论文 《后缀数组——处理字符串的有工具》(罗穗骞)

转载于:https://www.cnblogs.com/sing1ee/archive/2011/12/27/2764986.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值