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

题目:

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回  -1

示例:

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 06 处匹配。
第一个匹配项的下标是 0 ,所以返回 0

 思路:

暴力匹配:

时间复杂度:O(mxn)  空间复杂度:O(1)

 两层循环:一层遍历文本串,第二层遍历模式串,一一匹配,如果发现不匹配,则从文本串的第二位重新开始匹配,直至成功。

class Solution {
    public int strStr(String haystack, String needle) {
        int n = haystack.length();
        int m = needle.length();
        if(m > n){
            return -1;
        }
        // 当 needle 是空字符串时应当返回 0
        if(m == 0){
            return 0;
        }

        for (int i = 0; i <= n-m; i++) {
            boolean flag = true;
            for(int j=0;j<m;j++){
                if(haystack.charAt(i+j)!=needle.charAt(j)){
                    flag=false;
                    break;
                }
            }
            if(flag){
                return i;
            }
        }
        return -1;
    }
}

 

KMP算法(模式匹配):

时间复杂度:O(n+m)  空间复杂度:O(m)

KMP的经典思想:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。

前缀:包含首字母,不包含尾字母的所有子串

后缀:包含尾字母,不包含首字母的所有子串

比如模式串aabaaf:前缀(a、aa、aab、aaba、aabaa) 后缀(f、af、aaf、baaf、abaaf)

前缀表:找最长前后缀

a 0
aa 1
aab 0
aaba 1
aabaa 2
aabaaf 0

所以模式串aabaaf的前缀表为

a a b a a f
0 1 0 1 2 0

当aabaaf和文本串匹配冲突时,将j移动到最大前后缀后一位

求next数组的几种方式:

核心:遇见匹配冲突,要回退到合适位置

(1)前缀表   (2)整体右移 (3)整体减一

class Solution {
    public int strStr(String haystack, String needle) {
        int n = haystack.length();
        int m = needle.length();
        //当needle长度大于haystack时,返回-1
        if(m > n){
            return -1;
        }
        // 当needle是空字符串时,返回0
        if(m == 0){
            return 0;
        }
        //next数组
        int[] next = new int[m];
        getNext(next, needle);

        //初始化
        int j = 0;
        for (int i = 0; i < n; i++) {
            //当不匹配时,j移动到最大前后缀后一位
            while (j > 0 && needle.charAt(j) != haystack.charAt(i)) 
                j = next[j - 1];
            //匹配则一直往后移动
            if (needle.charAt(j) == haystack.charAt(i)) 
                j++;//这一步后,如果没有其他问题就回到i++循环
            //当匹配位数结束时,返回第一个匹配项下标
            if (j == m) 
                return i - m + 1;
        }
        return -1;

    }

    //构造next数组
    private void getNext(int[] next, String s) {
        //1.初始化
        int j = 0;
        next[0] = 0;
        for (int i = 1; i < s.length(); i++) {
            //2.前后缀不匹配
            while (j > 0 && s.charAt(j) != s.charAt(i)) 
                j = next[j - 1];
            //3.前后缀匹配
            if (s.charAt(j) == s.charAt(i)) 
                j++;
            //4.next数组更新
            next[i] = j; 
        }
    }
}

 

构造next数组代码解析:

 

 

 

 

 

 则,前面肯定有七位是重合的(如果next数组就是前缀表本身,则next[16]=7)

 此时比较第16位和第8位,如果相等,则next[17]=8+1=9

 

 

 

 

 

 

 

 

 

posted @ 2022-12-23 16:48  壹索007  阅读(19)  评论(0编辑  收藏  举报