字符串系列⑤ -- 重复的子字符串

题目概述

此题对应力扣的459.重复的子字符串,考察的也是KMP算法

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

示例:

输入: “abab”
输出: True
解释: 可由子字符串 “ab” 重复两次构成。

输入: “aba”
输出: False

输入: “abcabcabcabc”
输出: True
解释: 可由子字符串 “abc” 重复四次构成。 (或者子字符串 “abcabc” 重复两次构成。)

解题思路

这个题主要是运用到了KMP算法中的前缀表,因为这种找重复的子字符串的问题关键就在于整个字符串的最长公共前后缀,而这个信息前缀表的最后一项可以告诉我们。
如果这个题你想从一些不存在重复子字符串的例子(也就是输出false)中去找规律会有些困难,而从存在的例子中去寻找规律会更加简单。
在解题中我们假定我们使用的前缀表是不经过任何处理的(也就是没有整体减一或者整体右移这种操作),同时我们用next[]数组表示前缀表。我们可以知道next[s.length() - 1]得到的是整个字符串s的最长公共前后缀。而这个时候我们可以发现s.length - (next[s.length() - 1]) 表示一个最小周期的长度。
在这里我们可以通过几个例子来看一下:

情况集合s.length - (next[s.length() - 1]) 的值
abab2
abcabcabcabc3
abcabc3
acacacac2

如果你问为什么,有没有可能是一个一个巧合呢?除了使用数学归纳法证明之外,其实我们也可以简单的逻辑推理一下。假定现在有一个存在重复子字符串的字符串,那么他的最长公共前后缀也一定存在重复的子字符串(有人说为什么?根据前后缀的定义可以知道,前缀一定包含第一个字符,后缀一定包含最后一个字符,那么最长公共前后缀中就不可能不包含整数个最小周期,自己写一个例子一目了然。),此时再用整个字符串去截掉最长的公共前缀(最长的公共后缀同理)那么他剩下的一定是一个最短周期。可能你会说有没有可能是两个或者更多,如果是这样的话,那么就不满足最长公共前后缀了。

后面的也好想了,此时既然得到了最小周期,则:

  • 字符串长度 % 最小周期 不等于零 则说明不存在重复子字符串
  • 字符串长度 % 最小周期 等于零 则说明存在重复子字符串

到这里本应该结束了,但是有一个特殊情况我们容易遗漏,那就是当这个字符串的最长公共前后缀为零的时候,上面的条件他都成立,但很显然这种情况是不存在重复的子字符串的,所以在代码中我们要用条件语句去进行限制(也就是当此字符串的最长公共前后缀为0时,直接返回false)。

代码实现

 public boolean repeatedSubstringPattern(String s) {
        // 创建s的前缀表
        int[] next = new int[s.length()];
        builtNums(next,s);
        if(next[s.length() - 1] == 0){
            return false;
        }else if(s.length()%(s.length() - next[s.length() - 1]) == 0){
            return true;
        }else{
            return false;
        }
    }
    public void builtNums(int[] next,String needle){
        // 创建两个指针j,i,j指向后缀的尾部,i指向前缀的尾部
        int j = 0;
        next[j] = 0;
        for(int i = 1;i < needle.length();i++){
            //当两指针所指内容不同时,j指针回退
            while (needle.charAt(i) != needle.charAt(j) && j > 0){
                j = next[j - 1];
            }
            if(needle.charAt(i) == needle.charAt(j)){
                j++;
            }
            next[i] = j;
        }
    }

一个小注意点

在我刚做完这道题的时候,我发现既然你只用到了前缀表的最后一位,那是不是只用求最后一位就行了,干嘛要把整个前缀表计算出来影响解题的效率,后来我想了一下不行,因为你想算出前缀表的最后一位,少不了前缀表前面几位的帮助(在计算前缀表的时候涉及到了j指针的回退操作,而这个回退操作是要借助j前面一位的前缀表数据的),所以这种想法是行不通的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十八岁讨厌编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值