【代码随想录 | day09】(JavaScript) 字符串的操作:KMP算法、28.实现 strStr()、459.重复的字符串

93 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

  • 28.实现 strStr()
  • 459.重复的字符串
  • 字符串总结
  • 双指针回顾

KMP算法

什么是KMP?

KMP算法是一种改进的字符串匹配算法

核心:利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。

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

解决什么问题?

解决字符串匹配问题。

前后缀

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

所以说,我们要求的实际上是,一个字符串里面它的最长相等前后缀。

感觉KMP算法一时半会有点难懂,文末的参考文章里面放了其他大佬的讲解。溜了,等过段时间再来更新KMP算法的解题讲解。


28.实现 strStr()

题目链接:28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)

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

例如:haystack = "mississippi", needle = "issip"; 返回 4

我的解法

思路:

  1. 遍历字符串 haystack 的每个字母,直到遇到字母和字符串 needle 的第一个字母相同
  2. 取得字符串 needle 的长度,设为 needLen
  3. haystack 从该字母处截取,长度为 needLen 。所取子串放到 temp 里面
  4. 对比子串 temp 和 字符串 needle是否相同。
 var strStr = function (haystack, needle) {
   // 当needle里面的字符比haystack还多时,肯定找不到匹配下标
   if (haystack.length < needle.length) return -1;
 ​
   // 先求得needle的长度
   let needLen = needle.length;
   // let haystack = Array.from(haystack);
   for (let i = 0; i < haystack.length; i++) {
     if (haystack[i] == needle[0]) {
       let temp = haystack.slice(i, i + needLen)
       // console.log(temp);
       if (temp == needle) {
         return i
       } else {
         continue
       }
     }
   }
   // 如果for循环过后还是找不到匹配的,就返回-1
   return -1
 };

卡神解法

本题是KMP 经典题目。

 // 前缀表统一减一
 var strStr = function (haystack, needle) {
     if (needle.length === 0)
         return 0;
 ​
     const getNext = (needle) => {
         let next = [];
         let j = -1;
         next.push(j);
 ​
         for (let i = 1; i < needle.length; ++i) {
             while (j >= 0 && needle[i] !== needle[j + 1])
                 j = next[j];
             if (needle[i] === needle[j + 1])
                 j++;
             next.push(j);
         }
 ​
         return next;
     }
 ​
     let next = getNext(needle);
     let j = -1;
     for (let i = 0; i < haystack.length; ++i) {
         while (j >= 0 && haystack[i] !== needle[j + 1])
             j = next[j];
         if (haystack[i] === needle[j + 1])
             j++;
         if (j === needle.length - 1)
             return (i - needle.length + 1);
     }
 ​
     return -1;
 };

459.重复的字符串

题目链接:459. 重复的子字符串 - 力扣(LeetCode)

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

例如:输入: s = "abab",输出: true。解释: 可由子串 "ab" 重复两次构成。

我的解法

思路:

  1. 从字符串 s 中截取某一段字母。比如一开始截取第一个字母。
  2. 重复一定的次数压入数组中。这个次数怎么得来的呢?比如 'abab' 中,当 part = 'a' 时 ,arr 中通过push()不断压入'a' 。想要 arr 长度和 s 的长度相等,那么就得用字符串长度除以所截取的子串的长度,就能得到 arr = ['a' , 'a' , 'a' , 'a' ] 。
  3. 对比此数组转为字符串后和 s 是否相等。
 var repeatedSubstringPattern = function(s) {
   let len = s.length;
   // 这个 i 是用来截取字符串的,而且要求子串,所以不能等于 len
   for (let i = 1; i < len; i++) {
     let part = s.slice(0, i); // 截取到的字母片段
     let arr = [];
     // 片段字母不断压入数组中,注意重复的次数
     let times = len / part.length;
     while (arr.length < times) {
       arr.push(part);
     }
     // console.log(`字母片段是:${part},合成的数组是:${arr.join('')}`);
     if (arr.join('') == s) {
       return true
     }
   }
   return false
 };
重复子串1.jpg 重复子串2.jpg 重复子串3.jpg

卡神解法

 var repeatedSubstringPattern = function (s) {
     if (s.length === 0)
         return false;
 ​
     const getNext = (s) => {
         let next = [];
         let j = -1;
 ​
         next.push(j);
 ​
         for (let i = 1; i < s.length; ++i) {
             while (j >= 0 && s[i] !== s[j + 1])
                 j = next[j];
             if (s[i] === s[j + 1])
                 j++;
             next.push(j);
         }
 ​
         return next;
     }
 ​
     let next = getNext(s);
 ​
     if (next[next.length - 1] !== -1 && s.length % (s.length - (next[next.length - 1] + 1)) === 0)
         return true;
     return false;
 };

参考文章