leetcode【字符串—简单】459.重复的子字符串

2023-10-20 23:59

本文主要是介绍leetcode【字符串—简单】459.重复的子字符串,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 题目
  • 题解
  • 正文
    • NO1:整除比较法(初始思路)
    • NO2:KMP解决(核心,重点掌握)
    • NO3:超级整除法(参考大佬)
    • NO4、超简洁解法(看看就好)
  • 参考文章

前言

哈喽,我是长路,目前刚刚大三,方向是后端也偶尔捣鼓下前端,现在的主语言是Java。之前一大段时间都是在学习web开发的一些技术,就很久没有进行类似于数据结构、算法之类的学习与刷题,打算这段时间拾起来好好学一学、搞一搞。

这段时间也是机缘巧合看到草帽路飞的博客,加了自学群,正巧看到博主组织在群里组织了leetcode刷题打卡活动,我也就参与进来,为期一个月,打算坚持每天都花一些时间做一些题目,并通过博客的方式来进行记录。

目前跟着一个Github仓库刷题(leetcode):代码随想录leetcode刷题,当前为链表专题。



题目

题目来源leetcode

leetcode地址:459. 重复的子字符串,难度:简单。

题目描述(摘自leetcode):

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。示例 1:
输入: "abab"
输出: True
解释: 可由子字符串 "ab" 重复两次构成。示例 2:
输入: "aba"
输出: False示例 3:
输入: "abcabcabcabc"
输出: True
解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)

本地调试代码:

class Solution {public boolean repeatedSubstringPattern(String s) {...}public static void main(String[] args) {System.out.println(new Solution().repeatedSubstringPattern("aba"));}}


题解

正文

NO1:整除比较法(初始思路)

思路: 先自己想思路,有了思路直接code就行,当前提交没有看题解。

核心点:一个子串重复多次构成、只含有小写英文字母、长度不超过10000
思路:
1、确定每次重复的个数,这是我们能够确定的。例如"abababab",长度为8。那么每次重复个数情况有1、2、4。(for循环模一下即可判定)
2、直到重复个数之后就好办了,重新开个for循环从指定位置开始指定长度比较就ok,一旦一组比较下来都相同直接返回true,否则进行其他重复个数情况二次比较!

代码

public boolean repeatedSubstringPattern(String s) {String subStr = "";for (int i = 1; i < s.length(); i++) {//每i个数能够成对出现if(s.length() % i == 0){subStr = s.substring(0,i);int j = i;for (; j < s.length(); j+=i) {if(!Objects.equals(subStr,s.substring(j,j+i))){break;}}if(j == s.length()){return true;}}}return false;
}

image-20211031202208437

同样与这个思路一致,将直接取两个子串方式改为取两个指针来从左到右进行比较,看下是否有时间上的优化:

  • 注释的内容表示被替换了
public boolean repeatedSubstringPattern(String s) {//        String subStr = "";for (int i = 1; i < s.length(); i++) {if(s.length() % i == 0){//                subStr = s.substring(0,i);int j = i;for (; j < s.length(); j+=i) {//左右指针比较//                    if(!Objects.equals(subStr,s.substring(j,j+i))){//                        break;//                    }/*******直接取子串=>左右连续对比*******/int subStrCur = 0;int jCur = j;while(subStrCur != i){if(s.charAt(subStrCur) != s.charAt(jCur)){break;}subStrCur++;jCur++;}if(subStrCur != i){break;}/**************/}if(j == s.length()){return true;}}}return false;
}

image-20211101140019454

效果感觉不咋地,反而比我们使用subString()取字符串来的效率高,现在去看下其他人题解。



NO2:KMP解决(核心,重点掌握)

思路:利用KMP算法求得next数组,接着通过next数组最后一个元素的指向的最长重复前缀位置来进行判断是否当前字符串是否为重复的子字符串。

举例:
例1:s="abababab"  next[] = [-1, -1, 0, 1, 2, 3, 4, 5]next数组最后一个位置为5,指向原字符串中的倒数第三个,使用原字符串长度减去该位置-1,8-5-1=2,2就指的是对应子串的长度,接着使用字符串长度进行%,若是=0,表示其为重复子串。公式为:s.length() % (s.length() - targetPos - 1) == 0例2:s="ababcddc" next=[-1, -1, 0, 1, -1, -1, -1, -1]最后位置为-1,直接判定没有重复字符串。例3:s="ababcdab" next[] = [-1, -1, 0, 1, -1, -1, 0, 1]最后位置为1,同理代入,8%(8-1-1)=2,没有整%,所以直接判断没有

代码:时间复杂度O(n)

public int getNext(String s) {//KMP求得next数组        int next[] = new int[s.length()];int j = -1;next[0] = -1;for (int i = 1; i < s.length(); i++) {while (j >= 0 && s.charAt(i) != s.charAt(j + 1)) {j = next[j];}if (s.charAt(i) == s.charAt(j + 1)) {j++;}next[i] = j;}return next[s.length() - 1];
}public boolean repeatedSubstringPattern(String s) {//使用KMP取到最后一个元素重复元素前缀位置int targetPos = getNext(s);if (targetPos == -1) {return false;}if (s.length() % (s.length() - targetPos - 1) == 0) {return true;}return false;
}

image-20211101143900259



NO3:超级整除法(参考大佬)

使用KMP最后的执行耗时才击败了62%的人,接着就去看了看大佬的代码,真的是特别特别巧妙!

思路:相对于NO1的整除法,中间有很多没必要二次比较的情况,并且我是从小数重复串进行一一比对的,这里使用大数重复串比较,并且省去了一些没必要重复比较的情况。

假设某个字符串长度为8,按照正常思路顺序,先取最长重复串长度为4个4个比较、失败取2个2个、失败再取1个1个。
其实后面的两次就是没有必要的,我们举个例子:"abababac"①abab abac  (失败)②ab ab ab ac (失败)③a b a b a b b a c (失败)
仔细看第二步,假设它是重复串组成ab ab ab ab,那么就必然就有abab abab判断成功,那么①失败,下面的②③比较自然没有必要了。其规律就可以找到凡是某个最大子串匹配失败,那么其整除的情况(也就是②③)直接可以省略掉。上面说明了没必要重复比较的情况,下面再举一个长度为6的例子:"ababab"①aba bab (失败)②ab ab ab (成功)
对于该种情况,第②步是不用被省略的,因为第一组最大重复数量为3,其并不能整除2,那么对重复长度为2的自然会进行一一比较,这里就有一个问题,我们该如何进行比较?上面长度为6的有三组,难道要一个一个进行比?从大佬的代码中我又看到了解答,所有任意情况只要比较1次即可,对于②中取出abab abab这两个,刚开始我也贼蒙蔽,不过之后就感叹其妙的地方了。
重复长度2,第一组[0,6-2-1]=>[0,3] 第二组[2,6-1]=>[2-5],假设三组子字符串都先相同,那么任意两组之和也必然相同,借助这个要点,我们即可将多组比较化为2组比出结果。

代码

public boolean repeatedSubstringPattern2(String s) {int len = s.length();int parts = 2;//从2开始,之后取子串len/2、len/3,保证子串从最大长度开始进行比较重复int noRepeatLen = len;while (noRepeatLen > 1) {if (noRepeatLen % parts == 0) {int k = len / parts;//子串长度//取出两组进行比较if (Objects.equals(s.substring(0, len - k), s.substring(k))) {return true;}//去除重复的整除情况,在除数上进行操作noRepeatLen /= parts;while (noRepeatLen % parts == 0) {noRepeatLen /= parts;}}parts++;}return false;
}

image-20211101182730440



NO4、超简洁解法(看看就好)

好家伙,两行解决?还是很妙的。举两个例子就可以看懂了。

思路

① s="abac" 不重复情况  "ababca"s+s = "abacabac",去头去尾"bacaba"
② s="abab"s+s = "abababab",去头去尾"bababa" √
③ s="aaaa"s+s = "aaaaaaaa",去头去尾"aaaaaa" √这种方式很巧妙,通过拼接方式去头去尾看其中是否存在原字符串。若是有重复的必然拼接中有对应重复的!

代码

public boolean repeatedSubstringPattern(String s) {String str = s + s;return str.substring(1, str.length() - 1).contains(s);
}

image-20211101185001905



参考文章

[1]. leetcode题解

[2]. 代码随想录—459.重复的子字符串


我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
Q群:851968786 我们可以一起探讨学习
注明:转载可,需要附带上文章链接

这篇关于leetcode【字符串—简单】459.重复的子字符串的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/250530

相关文章

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

C#从XmlDocument提取完整字符串的方法

《C#从XmlDocument提取完整字符串的方法》文章介绍了两种生成格式化XML字符串的方法,方法一使用`XmlDocument`的`OuterXml`属性,但输出的XML字符串不带格式,可读性差,... 方法1:通过XMLDocument的OuterXml属性,见XmlDocument类该方法获得的xm

Redis 多规则限流和防重复提交方案实现小结

《Redis多规则限流和防重复提交方案实现小结》本文主要介绍了Redis多规则限流和防重复提交方案实现小结,包括使用String结构和Zset结构来记录用户IP的访问次数,具有一定的参考价值,感兴趣... 目录一:使用 String 结构记录固定时间段内某用户 IP 访问某接口的次数二:使用 Zset 进行

Spring Boot 整合 ShedLock 处理定时任务重复执行的问题小结

《SpringBoot整合ShedLock处理定时任务重复执行的问题小结》ShedLock是解决分布式系统中定时任务重复执行问题的Java库,通过在数据库中加锁,确保只有一个节点在指定时间执行... 目录前言什么是 ShedLock?ShedLock 的工作原理:定时任务重复执行China编程的问题使用 Shed

JSON字符串转成java的Map对象详细步骤

《JSON字符串转成java的Map对象详细步骤》:本文主要介绍如何将JSON字符串转换为Java对象的步骤,包括定义Element类、使用Jackson库解析JSON和添加依赖,文中通过代码介绍... 目录步骤 1: 定义 Element 类步骤 2: 使用 Jackson 库解析 jsON步骤 3: 添

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的