一道有趣的题----重复的子字符串

题目如下:

        给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

例如:s="ababab";

        结果为:true(s由"ab"子字符串重复构成);

        s="abc";

        结构为:false(没有子字符串能够重复构成s)

(力扣原题链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台)

函数大体框架如下:

        那么我们该如何实现这个函数的功能呢?

        这里我介绍一种非常有趣的解题方法:采用KMP算法来求解该题(不知道KMP算法的同学得自己先明白什么是KMP算法,最起码要知道next数组的涵义----最长相等前后缀构成的集合).大体思路如下:我们先计算出s的next数组,然后拿到next数组中的最末尾元素,也就是next[s.size()-1],然后将s的长度减去这个值,去看这个差值能不能被s.size()整除,如果能那么这个s就是能被某个子串重复构成,如果不能就不能被重复构成,具体的代码如下:

        

        这里我就不讲如何求s的前缀表了,主要讲为什么可以这样求解.

        我们通过next数组可以发现:若s是由重复的子串构成那么,那么这个子串就一定是:最长相等的前后缀的不相交的部分:

        

        由上图可知:最长相等前后缀的除去相交的部分就只剩下"ab",而"ab"刚好是能重复构成s的子串.推广到其他结果为true的s也是一样的.那为什么呢?

        首先我们假设最长相等的前,后缀分别为L与R,由于L=R,所以L[0]=R[0],L[1]=R[1],而R[0]和R[1]又分别等于S[2]和S[3],所以得:L[0]=S[2],L[1]=S[3],即为:S[0]=S[2],S[1]=S[3],这出现了重复,所以可以得到这个小段"ab"就是我们所要找的子串.

        而记录了最长的相等前后缀的长度有刚好是next数组的最末尾的元素,即next[s.size()-1],然后我们在利用s.size()减去这个长度,就能得到目标子串"ab"的长度.由于s是"ab"重复构成的,那么s.size就一定能被2整除,对其他的结果为true的s来说也同样成立.

        以上推导还有一个问题,就是:那为什么结果为false的s,s,szie()就不能整除(s.size-next[s.size()-1])?结果还真不能整除.

        结果为false的s可以看做两部分组成,相等的前后缀,和一段中间子串,例如:s="aabaa",那么s就可以看做是由"aa"和"b"构成的,若第一部分的子串的长度为m,中间段子串长度为n,那么s.size()=2*m+n,而next[s.size-1]的值刚好就是m,所以s.size()-next[s.size()-1]=m+n,而m+n一定是大于1/2的s.size()的,所以(2*m+n)/m+n一定不能被整除(若一个数为n,则n除任何一个大于n/2且小于n的数,都不能得到整数,大家可以试着证明一下).所以我们s.size%(s.size-next[s.size()-1])是否等于0,来判断这个s到底是true,还是false.

        以上就这道题解法的原理分析,主要还是利用了next数组中的最长相等前后缀这个神奇的特性,整体上思路还是比较抽象的,希望大家可以多用纸和笔画一画会好理解很多的.

        

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值