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

举报
十八岁讨厌编程 发表于 2022/08/06 01:12:51 2022/08/06
【摘要】 目录 题目概述解题思路代码实现一个小注意点 题目概述 此题对应力扣的459.重复的子字符串,考察的也是KMP算法 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给...

题目概述

此题对应力扣的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;
        }
    }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

一个小注意点

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

文章来源: blog.csdn.net,作者:十八岁讨厌编程,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/zyb18507175502/article/details/122321978

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。