Day09~28. 实现 strStr()、459.重复的子字符串

120 阅读5分钟

摘要

本文主要介绍了KMP算法,并提供了LeetCode中28. 实现 strStr()、459. 重复的子字符串题目的暴力解法。最后,我们进行了字符串算法的总结,并回顾了双指针技巧。

1、KMP算法

KMP(Knuth-Morris-Pratt)算法是一种用于字符串匹配的高效算法,用于在一个主文本字符串中查找一个较短的子串。它的主要思想是通过预处理模式字符串(要查找的子串),构建一个部分匹配表(也称为最长前缀后缀表),然后使用这个表来在主文本字符串中跳过不必要的比较,从而加速字符串匹配的过程

KMP算法的关键是构建部分匹配表,它在模式字符串中查找每个前缀(包括自身)的最长前缀后缀的长度。这个表告诉我们,当在主文本中发生不匹配时,应该将模式字符串向右移动多少个位置,以便继续比较。

KMP算法的步骤如下:

  1. 预处理模式字符串,构建部分匹配表。
  2. 在主文本字符串中从左到右扫描,比较主文本和模式字符串的字符。
  3. 当发生不匹配时,根据部分匹配表的值来移动模式字符串,而不是回溯主文本字符串的指针。
  4. 重复步骤2和步骤3,直到找到匹配或者遍历完主文本字符串。

KMP算法的优点是它避免了在不匹配时不必要的字符比较,从而提高了匹配效率。这使得KMP算法在大文本中查找子串时表现出色。

总的来说,KMP算法是一种经典的字符串匹配算法,用于在时间复杂度为O(N+M)的情况下,在一个长文本中高效地查找一个模式串。

2、28. 实现 strStr()

2.1 暴力法

思路

暴力法:双层遍历,外层遍历haystack,从下标0开始到(len1-len2),内层遍历needle, 比较haystac下标i+j 的元素与needle下标j 的元素, 如果匹配直到needle结束,则返回下标i,如果不匹配,内层遍历break

代码

    public int strStr(String haystack, String needle) {
        int len1 = haystack.length();
        int len2 = needle.length();
​
        for(int i=0; i<len1-len2+1; i++) {
            for(int j=0; j<len2; j++) {
                if(haystack.charAt(i+j) != needle.charAt(j)) {
                    break;
                }
                
                if(j == len2-1) {
                     return i;
                }
            }
        }
        return -1;
    }

2.2 KMP *

3、459.重复的子字符串

3.1 暴力法

思路

暴力法,外层遍历,i代表子串的大小,字符串s 是字串的倍数才进行内层遍历,定义j从i开始遍历至结束 如果s.charAt(j) != s.charAt(j-i) 则代表不是重复的字符串,内存循环break

  • s.charAt(j) != s.charAt(j-i)

代码

    public boolean repeatedSubstringPattern(String s) {
        int len = s.length();
​
        for(int i=1; i<=len/2; i++) {
            if(len % i == 0) {
                boolean flag = true;
                for(int j=i; j<len; j++) {
                    if(s.charAt(j) != s.charAt(j-i)) {
                        flag = false;
                        break;
                    }
                }
​
                if(flag) {
                    return true;
                }
            }
        }
        return false;
    }

3.2 KMP *

4、字符串总结

以下是对字符串算法专题的总结,包括各个算法题的主要思路和关键点:

  1. 344. 反转字符串

    • 使用双指针法,一个指向字符串的开头,一个指向结尾,不断交换字符直至遍历完整个字符串。
  2. 541. 反转字符串 II

    • 基于反转字符串的思路,但这次要求只反转每个2k个字符中的前k个字符,保留其余字符不变。
  3. 剑指Offer 05. 替换空格

    • 遍历原字符串,将空格替换为"%20"。
  4. 151. 翻转字符串里的单词

    • 先翻转整个字符串,再依次翻转每个单词。
  5. 剑指Offer58-II. 左旋转字符串

    • 将字符串分为两部分,分别翻转,然后合并翻转后的字符串。
  6. 28. 实现 strStr()

    • 使用KMP算法或暴力匹配算法,找出目标字符串在原字符串中的位置。
  7. 459. 重复的子字符串

    • 将原字符串复制一份,去掉首尾字符,如果复制后的字符串中仍包含原字符串,说明原字符串是由一个子字符串重复多次构成。

这些算法题涵盖了字符串处理中的常见问题,包括反转、替换、翻转单词、子字符串匹配等。总结时可以强调不同题目的不同解决思路,例如使用双指针、KMP算法、复制拼接等。此外,注意字符串问题的常见技巧和注意事项,如处理边界情况、字符串为空等。

5、双指针回顾

数组篇:

  • 使用双指针法能有效提高数组操作的效率,尤其在移除元素时。
  • 避免使用erase操作,因为它的时间复杂度是O(n),使用双指针可以将操作变成O(n)。

字符串篇:

  • 双指针法可用于反转字符串、替换空格、删除冗余空格等操作,时间复杂度为O(n)。
  • 从后向前填充字符串可以提高效率,避免O(n^2)的算法。

链表篇:

  • 使用双指针法翻转链表,只需改变next指针的指向,无需重新定义新链表。
  • 快慢指针是判断链表是否有环的经典方法,可用于找到环的入口。

N数之和篇:

  • 哈希法适用于两数之和问题,但不适用于三数之和等问题,因为去重和剪枝困难。
  • 使用双指针法可以将时间复杂度从O(n^2)降低到O(n)。

总结:

  • 本文介绍了九道LeetCode题目,展示了双指针法的应用。
  • 建议练习这些题目,熟练掌握双指针法,以提高算法解题效率。

参考资料

代码随想录-实现 strStr()

代码随想录-重复的子字符串

代码随想录-双指针总结