找出字符串中第一个匹配项的下标&重复的子字符串

一、 找出字符串中第一个匹配项的下标

28. 找出字符串中第一个匹配项的下标

1.方法概述

  • 主要就四个步骤初始化next数组,前后缀不相等的情况,前后缀相等的情况,更新next数组。分别定义一个i(主串),j(子串)下标来表示遍历主串和子串的时开始位置,两个下标都从0下标开始。判断当主串和子串长度为0或者主串子串为null直接返回-1。创建一个大小等于子串的数组,使用getNext函数拿到next数组。当i<lenStr(主串长度)和j<lenSub(子串长度)时循环遍历(while),当找到相同字符时,i++,j++。不相同时,i不回退,j回退到 j = next[j]。接下来如果j>=lenSub长度则返回i-j表示全部匹配完成。否则返回-1。

2.具体实现

Java实现

点击查看代码
class Solution {
    public void getNext(int[] next,String s){
        next[0] = -1;
        int i = 2;
        int j = 0;
        while(i<s.length()){
            if((j == -1) || s.charAt(j) == s.charAt(i-1)){
                next[i] = j+1;
                i++;
                j++;
            }else{
                j = next[j];
            }
        }
    }

    public int strStr(String haystack, String needle) {
       if(haystack == null || needle == null){
           return -1;
       }
       if(needle.length() == 0 || haystack.length() == 0){
           return -1;
       }
       int i = 0;
       int j = 0;
       int[] next = new int[needle.length()];
       getNext(next,needle);
       while(i < haystack.length() && j < needle.length()){
           if((j==-1)||(haystack.charAt(i) == needle.charAt(j))){
               i++;
               j++;
           }else{
               j = next[j];
           }
       }
       if(j >= needle.length()){
           return i-j;
       }else{
           return -1;
       }
    }
}

3.要点总结

(1)几个概念的说明

什么是KMP,KMP是用来处理什么问题的?

  • KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。

前缀,后缀是什么?

  • 前缀:是指不包含最后一个字符的所有以第一个字符开头的连续子串。
  • 后缀:是指不包含第一个字符的所有以最后一个字符结尾的连续子串。

前缀表是什么?

  • 是用来记录下标j(子串)之前(包括j)的字符串中,有多大长度的相同前缀后缀。
    image
    image

什么是next数组?

  • 一般是前缀表右移一位,初始位置为-1后作为next数组。
    image

(2)next数组处理的问题,以及如何初始化

  • KMP算法的关键在next[]数组,也就是用 next[j] = k来表示,不同的 j 来对应一个 K 值, 这个 K 就是你将来要移动的 j要移动的位置。

K 的值是这样求的:

  • 规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 字符开始,另一个以 j-1 下标字符结尾。
  • 不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始;也就数组整体右移一位,首位赋初值为-1.
  • 假设当 next[j] = k 时有 p[0]~p[k-1] = p[x]~p[j-1] 即有 k-1-0 = j-1-x,x = j-k ,即p[0]~p[k-1] = p[j-k]~p[j-1]。如果p[j] == p[k],则有next[j+1] = k+1,如果p[j] != p[k]则next[j+1] = ?等于多少呢,就需要一直回退直到找到p[j] == p[k],然后有又有next[i+1] = k+1。

二、重复的子字符串

459. 重复的子字符串

1.方法概述

-需要在预处理next[]数组后,判断n是否为n-next[n-1]-1的倍数。

2.具体实现

Java实现

点击查看代码
class Solution {
    public boolean repeatedSubstringPattern(String s) {
        if (s.equals("")) return false;
        int len = s.length();
        s = " " + s;
        char[] chars = s.toCharArray();
        int[] next = new int[len + 1];
        for (int i = 2, j = 0; i <= len; i++) {
            while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
            if (chars[i] == chars[j + 1]) j++;
            next[i] = j;
        }
        if (next[len] > 0 && len % (len - next[len]) == 0) {
            return true;
        }
        return false;
    }
}

3.要点总结

(1)为什么使用KMP来解决该题?

  • 在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串,就是最小重复单位。我们可以利用KMP来找到该最小重复单位。
    image

(2)如何确定最小重复子串?

  • 因为字符串s的最长相同前后缀的长度一定是不包含字符串本身,最所以最长相等前后缀的长度为next[len - 1] +1,如果len%(len-(next[len-1]+1)) == 0,则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。
posted @ 2023-02-20 22:18  路虽遥疾风归  阅读(166)  评论(0)    收藏  举报