Leetcode | 459 重复的字符串9ms的kmp算法java

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

示例 1:

输入: “abab”

输出: True

解释: 可由子字符串 “ab” 重复两次构成。
示例 2:

输入: “aba”

输出: False
示例 3:

输入: “abcabcabcabc”

输出: True

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/repeated-substring-pattern

代码思路:
这道题的思路如果熟悉kmp的算法,这道题还是比较简单的。
kmp的算法在于找出字符串中出现的前缀。
例如:
1 2 3 4 5 6 7 8 9 10 11
a b c a a a a b c d d
-1 0 0 0 0 0 0 1 2 0 0
上面的例子中在789号位置上匹配前缀abc成功,所以就可以知道这个字符串中有自己的前缀abc出现在字符串中。

回到这道题,如果是一个由一个前缀重复构成的字符串,那么由kmp算法分析后的数组特点如下:

  • 开始的前缀一定是-1 0 0 0
  • 后续一定是逐步增一的数组

例如:

0 1 2 3 4 5 6 7 8
a b c a b c a b c
-1 0 0 0 1 2 3 4 5

为了匹配整个字符串所以要对这个字符串进行以下处理。

0 1 2 3 4 5 6 7 8 9
a b c a b c a b c #
-1 0 0 0 1 2 3 4 5 6

然后就可以知道匹配到最后的前缀是什么,
9-6=3,所以前三个字符构成的字符串就是被重复的子字符串。

然后再次进行检索,整个字符串是否确实由这个前缀重复组成的,返回对应结果即可。

public boolean repeatedSubstringPattern1(String s) {
        int curr=0;
        int count=-1;
        s=s+"#";
        int[] temp=new int[s.length()];
        temp[0]=-1;
       //kmp算法
        for(int i=1;i<s.length();){
            if(count==-1||s.charAt(i-1)==s.charAt(count)){
                temp[i]=++count;
                i++;
            }else{
                
                count=temp[count];
            }
        }
        curr=0;
        s=s.substring(0,s.length()-1);
        //如果最后是一个0,说明字符串最后一个字符不对,所以返回错误
        if(temp[s.length()]==0)return false;
        //然后对整个字符串进行比对
        for(int i=s.length()-temp[s.length()];i<s.length();i++){
            if(curr==s.length()-temp[s.length()]){
                curr=0;
            }
            if(s.charAt(i)==s.charAt(curr)){
                curr++;
            }else{
                return false;
            }
        }
        //比对完成后,根据比对返回结果
        if(curr==s.length()-temp[s.length()]){
            return true;
        }else{
            return false;
        }
      
    }

结果如下:
在这里插入图片描述

代码分析:
时间复杂度:O(2n),搜索了两遍。

进一步分析:
如果是符合要求的字符串找到的子串的长度一定是字符串总长度的约数。
a b c e e e a b c #
-1 0 0 0 0 0 0 1 2 3
前缀的计算9-3=6,所以重复的前缀就是abceee,因为后续只有abc所以说明如果有后续可能的被重复的前缀是abceee,但是现在总的字符长度只有9,这个子串长度有6所以这个字符串不符合要求。

改进代码如下:

public boolean repeatedSubstringPattern2(String s) {
	       
        int curr=0;
        int count=-1;
        s=s+"#";
        int[] temp=new int[s.length()];
        temp[0]=-1;
       
        for(int i=1;i<s.length();){
            if(count==-1||s.charAt(i-1)==s.charAt(count)){
                temp[i]=++count;
                i++;
            }else{
                
                count=temp[count];
            }
        }
        s=s.substring(0,s.length()-1);
        return temp[s.length()] != 0 && s.length() % (s.length() - temp[s.length()] ) == 0;
      
    }

结果如下:
在这里插入图片描述
代码分析:
时间复杂度:O(n)。只有kmp算法对字符串进行了一次扫描。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值