代码随想录day9|28. 实现 strStr() 、459. 重复的子字符串

KMP算法基础

文本串:aabaabaaf
模式串:aabaaf
  • 问题需求:需要找到文本串中是否存在模式串的匹配问题

  • 前缀与后缀:对于字符串aabaaf,前缀是不包含最后一个字符的所有字串,后缀是不包含第一个字符的所有字串

前缀:a、aa、aab、aaba、aabaa
后缀:f、af、aaf、baaf、abaaf
  • 前缀表:前缀表是用于找到字符串的所有字串最长相等前后缀的一个数组,其作用是用于进行字符串匹配。

例如:

a: 0 只有一个字符,为0
aa: 1 前缀为a 后缀为a 相等,最长相等前后缀为1
aab :0 前缀为a、a; 后缀为b、ab,最长相等前后缀长度为0
aaba: 1 前a、aa、aab;后a、ba、aba,最长相等前后缀长度为1
aabaa: 2 前a、aa、aab、aaba;后a、aa、baa、abaa,最长相等前后缀长度为2
aabaaf: 0 前a、aa、aab、aaba、aabaa;后f、af、aaf、baaf、abaaf,最长相等前后缀长度为0

因此可以得到前缀表,即next数组。next = [0, 1, 0, 1, 2, 0]

  • 前缀表实现代码:

private int[] returnNext(String s){
    int[] next = new int[s.length()];
    //j指向前缀末尾
    int j = 0;
    //i指向后缀末尾
    for(int i = 1; i < s.length(); i++){
        //处理i与j位置值不等的情况,此时需要j不断回退,进入while循环
        while(j > 0 && s.charAt(i) != s.charAt(j)){
            j = next[j - 1];
        }
        //处理i与j位置值相等的情况,此时j需要前进,进行j++
        if(s.charAt(i) == s.charAt(j)){
            j++;
        }
        //更新next数组的值
        next[i] = j;
    }

    return next;
}

28. 实现 strStr()

题目链接

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1
  • 分析:找到模式串needle的前缀表next数组,随后比较haystack 与needle数组,当二者相等时,继续向后比较,遇到不等时,根据next数组的值进行回退。

  • 代码:

public int strStr(String haystack, String needle) {
    int[] next = getNext(needle);
    int start = -1;
    int end = -1;
    int j = 0;
    for(int i = 0; i < haystack.length(); i++){
        if(haystack.charAt(i) == needle.charAt(j)){
            j++;
            if(j < needle.length()){
                continue;
            }else{
                end = i;
                break;
            }
        }
        while(j > 0 && haystack.charAt(i) != needle.charAt(j)){
            j = next[j - 1];
        }
        if(haystack.charAt(i) == needle.charAt(j)){
            j++;
        }
        if(j == needle.length() - 1 && needle.length() - 1 > 0){
            end = i;
        }

    }
    if(end != -1){
        start = end - needle.length() + 1;
    }
    return start;

}
int[] getNext(String s){
    int[] next = new int[s.length()];
    int j = 0;
    for(int i = 1; i < s.length(); i++){
        while(j > 0 && s.charAt(i) != s.charAt(j)){
            j = next[j - 1];
        }
        if(s.charAt(i) == s.charAt(j)){
            j++;
        }
        next[i] = j;
    }

    return next;
}
  • 因为需要找到模式串在文本串中的起始位置,所以将start和end都定义为 - 1,并且遍历最终如果end不为 - 1的话返回结果为:end - needle.length() - 1。

  • j是模式串needle的指针, i是文本串haystack的指针。进入for循环比较后,首先比较当前两个位置的值是否相等:

    if(haystack.charAt(i) == needle.charAt(j)){
        j++;
        if(j < needle.length()){
            continue;
        }else{
            end = i;
            break;
        }
    }

这里需要判断,只有当j还没有遍历到needle的末尾时,继续遍历,否则证明已经遍历到模式串末尾了并且还相等,证明已经匹配结束,将end值更新为当前的i,并退出循环。

  • 针对i与j位置不相等的情况,按照next数组进行回退,注意是一直回退直到相等

    while(j > 0 && haystack.charAt(i) != needle.charAt(j)){
        j = next[j - 1];
    }
    if(haystack.charAt(i) == needle.charAt(j)){
        j++;
    }

在while回退后紧跟的这个if判断的目的是,为了判断导致while退出循环的是否是已经在next数组中找到与当前i相等的在模式串中的位置,即排除j<0 的情况。此时因为已经找到了与当前i相等的j,对j进行更新,做j++。

  • 最后当j遍历到needle末尾,退出循环,更新end值,排除单个字符的needle情况

    if(j == needle.length() - 1 && needle.length() - 1 > 0){
        end = i;
    }

459. 重复的子字符串

题目链接

  • 分析:对于由重复子字符串构成的字符串:

abababab
next = [0, 0, 1, 2, 3, 4, 5, 6]

其最长相等前后缀长度为6,也就是说相等的最长前后缀都是:ababab,那么将原字符串长度与该最长相等前后缀长度相减,即得到重复字串ab。也就是说当len % (len - next[len - 1]) == 0时,该字符串由重复字串构成。

  • 代码:

public boolean repeatedSubstringPattern(String s) {
    if(s.length() <= 1){
        return false;
    }
    int len = s.length();
    int[] next = returnNextArray(s);
    int max = 0;
    return next[len - 1] > 0 && len % (len - next[len - 1]) == 0? true: false;
    
}

private int[] returnNextArray(String s){
    int[] next = new int[s.length()];
    int j = 0;
    next[0] = 0;
    for(int i = 1;i < next.length;i++){
        while(j > 0 && s.charAt(i) != s.charAt(j)){
            j = next[j - 1];
        }
        if(s.charAt(i) == s.charAt(j)){
            j++;
        }
        next[i] = j;
    }

    return next;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值