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

导读:本篇文章讲解 字符串系列⑤ — 重复的子字符串,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

也许你感觉自己的努力总是徒劳无功,但不必怀疑,你每天都离顶点更进一步。今天的你离顶点还遥遥无期。但你通过今天的努力,积蓄了明天勇攀高峰的力量。加油!

题目概述

此题对应力扣的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]) 的值
abab 2
abcabcabcabc 3
abcabc 3
acacacac 2

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

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

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

到这里本应该结束了,但是有一个特殊情况我们容易遗漏,那就是当这个字符串的最长公共前后缀为零的时候,上面的条件他都成立,但很显然这种情况是不存在重复的子字符串的,所以在代码中我们要用条件语句去进行限制(也就是当此字符串的最长公共前后缀为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前面一位的前缀表数据的),所以这种想法是行不通的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/122336.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!