卷进大厂系列之LeetCode刷题笔记:找到重复的子字符串(简单)

本文介绍了如何使用KMP算法解决检查字符串是否由重复子串构成的问题。首先,概述了字符串和KMP算法的基础知识,然后详细解析了两种解题思路:暴力枚举法和KMP算法实现。暴力解法通过遍历所有可能的子串进行比较,而KMP算法则通过构造next数组来判断字符串是否具有重复子串,并给出了具体的Java代码实现。
摘要由CSDN通过智能技术生成

学算法,刷力扣,加油卷,进大厂!

题目描述

力扣题目链接

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

示例 1:

输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。

示例 2:

输入: s = "aba"
输出: false

示例 3:

输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)

提示:

  • 1 <= s.length <= 104
  • s 由小写英文字母组成

涉及知识点

这看着是一道非常简单的题目,当然了使用暴力解法也是可以解决的。不过,这道题目涉及了一个很重要的思想,就是KMP算法。这道题目涉及的知识点是:

  • 字符串是一种特殊的数组,其可以放入Char数组中
  • KMP算法

KMP算法:一种改进的字符串匹配算法,其是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。(更详细的内容可自行查阅)

而这道题目之所以说涉及了KMP算法,就是next数组的一个理解,next数组记录的就是最长相等前后缀的长度,如果next[len - 1] != -1,则说明字符串有相等的前后缀。那么,如果len % (len - (next[len - 1] +1)== 0,则说明(数组长度-最长相等前后缀的长度)正好被数组长度整除,即该字符串中有重复的子字符串。

字符数组长度为length,最长相等前后缀的长度next[len - 1] +1

那么根据题目,我们可以提炼的关键点:

  • 字符串非空
  • 找字符串的子串,其满足重复子串可构成原字符串

当然了,这道题目的暴力解法就是枚举的形式,毕竟简单可暴力,为何不试一下呢?

题目解答

Java题解一

思路分析

先来一个暴力解法,小试牛刀。这道题目的暴力解法就是枚举所有子串,然后然后遍历字符串s,看下是否满足题设,可通过比较s.charAt(i) 与 s.charAt(i-len)是否相等来判断。

具体思路如下:

  • 定义一个字符串的长度n,方便后面用
  • 开始遍历字符串了,开始假定满足条件的子串为1/2,然后是1/3等,即子串从大往小了试
  • 得出子串的长度后,进行比较
  • 用s.charAt(j) != s.charAt(j-len)); 第j个和下一个子串的第j个比较
class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int n = s.length(); //字符串长度

        //遍历字符串
        for(int i = 2; i <= n;i++){
            //长度满足是i的倍数的情况下(子串从大往小了试)
            if(n%i == 0){
                boolean temp = true; 
                int len = n/i; //子串的长度
                //判断是否满足重复子串
                for(int j = len;j < n;j++){
                    if(s.charAt(j) != s.charAt(j-len)){ //第j个和下一个子串的第j个
                        temp = false;
                        break;
                    }
                }
                if(temp) return true;
            }
        }
        return false;
    }
}

在这里插入图片描述

Java题解二

思路分析

这个思路是通过KMP算法的形式来解决。即数组长度减去最长相等前后缀的长度相当于第一个重复子字符串的长度,也就是一个重复周期的长度,如果这个周期可以被整除,则说明整个数组就是这个周期的循环,则说明满足题目条件。

具体思路如下:

  • 定义数组长度len,方便使用
  • 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了
  • 构造 next 数组过程,j从0开始(空格),i从2开始进行匹配:
    -(1) 匹配不成功,j回到前一位置 next 数组所对应的值
    -(2)匹配成功,j往后移,更新 next 数组的值
  • 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
class Solution {
    //KMP
    public boolean repeatedSubstringPattern(String s) {
        int len = s.length();
        // 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了
        s = " " + s;
        char[] chars = s.toCharArray();
        int[] next = new int[len + 1];

        // 构造 next 数组过程,j从0开始(空格),i从2开始
        for (int i = 2, j = 0; i <= len; i++) {
            // 匹配不成功,j回到前一位置 next 数组所对应的值
            while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
            // 匹配成功,j往后移
            if (chars[i] == chars[j + 1]) j++;
            // 更新 next 数组的值
            next[i] = j;
        }

        // 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
        if (next[len] > 0 && len % (len - next[len]) == 0) {
            return true;
        }
        return false;
    }
}

在这里插入图片描述

评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Faith_xzc

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

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

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

打赏作者

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

抵扣说明:

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

余额充值