diff --git a/problems/0028.实现strStr.md b/problems/0028.实现strStr.md index 19d16e9f..fc222441 100644 --- a/problems/0028.实现strStr.md +++ b/problems/0028.实现strStr.md @@ -94,7 +94,7 @@ next数组就是一个前缀表(prefix table)。 **前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。** -为了清楚的了解前缀表的来历,我们来举一个例子: +为了清楚地了解前缀表的来历,我们来举一个例子: 要在文本串:aabaabaafa 中查找是否出现过一个模式串:aabaaf。 @@ -110,9 +110,9 @@ next数组就是一个前缀表(prefix table)。 ![KMP详解1](https://code-thinking.cdn.bcebos.com/gifs/KMP%E7%B2%BE%E8%AE%B21.gif) -动画里,我特意把 子串`aa` 标记上了,这是有原因的,大家先注意一下,后面还会说道。 +动画里,我特意把 子串`aa` 标记上了,这是有原因的,大家先注意一下,后面还会说到。 -可以看出,文本串中第六个字符b 和 模式串的第六个字符f,不匹配了。如果暴力匹配,会发现不匹配,此时就要从头匹配了。 +可以看出,文本串中第六个字符b 和 模式串的第六个字符f,不匹配了。如果暴力匹配,发现不匹配,此时就要从头匹配了。 但如果使用前缀表,就不会从头匹配,而是从上次已经匹配的内容开始匹配,找到了模式串中第三个字符b继续开始匹配。 @@ -157,7 +157,7 @@ next数组就是一个前缀表(prefix table)。 以下这句话,对于理解为什么使用前缀表可以告诉我们匹配失败之后跳到哪里重新匹配 非常重要! -**下标5之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀 和 后缀字符串是 子字符串aa ,因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面从新匹配就可以了。** +**下标5之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀 和 后缀字符串是 子字符串aa ,因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面重新匹配就可以了。** 所以前缀表具有告诉我们当前位置匹配失败,跳到之前已经匹配过的地方的能力。 @@ -199,7 +199,7 @@ next数组就是一个前缀表(prefix table)。 所以要看前一位的 前缀表的数值。 -前一个字符的前缀表的数值是2, 所有把下标移动到下标2的位置继续比配。 可以再反复看一下上面的动画。 +前一个字符的前缀表的数值是2, 所以把下标移动到下标2的位置继续比配。 可以再反复看一下上面的动画。 最后就在文本串中找到了和模式串匹配的子串了。 @@ -211,7 +211,7 @@ next数组就可以是前缀表,但是很多实现都是把前缀表统一减 为什么这么做呢,其实也是很多文章视频没有解释清楚的地方。 -其实**这并不涉及到KMP的原理,而是具体实现,next数组即可以就是前缀表,也可以是前缀表统一减一(右移一位,初始位置为-1)。** +其实**这并不涉及到KMP的原理,而是具体实现,next数组既可以就是前缀表,也可以是前缀表统一减一(右移一位,初始位置为-1)。** 后面我会提供两种不同的实现代码,大家就明白了。 @@ -231,7 +231,7 @@ next数组就可以是前缀表,但是很多实现都是把前缀表统一减 其中n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是O(n),之前还要单独生成next数组,时间复杂度是O(m)。所以整个KMP算法的时间复杂度是O(n+m)的。 -暴力的解法显而易见是O(n × m),所以**KMP在字符串匹配中极大的提高的搜索的效率。** +暴力的解法显而易见是O(n × m),所以**KMP在字符串匹配中极大地提高了搜索的效率。** 为了和力扣题目28.实现strStr保持一致,方便大家理解,以下文章统称haystack为文本串, needle为模式串。 @@ -251,7 +251,7 @@ void getNext(int* next, const string& s) 2. 处理前后缀不相同的情况 3. 处理前后缀相同的情况 -接下来我们详解详解一下。 +接下来我们详解一下。 1. 初始化: @@ -613,12 +613,12 @@ class Solution { public void getNext(int[] next, String s){ int j = -1; next[0] = j; - for (int i = 1; i=0 && s.charAt(i) != s.charAt(j+1)){ + for (int i = 1; i < s.length(); i++){ + while(j >= 0 && s.charAt(i) != s.charAt(j+1)){ j=next[j]; } - if(s.charAt(i)==s.charAt(j+1)){ + if(s.charAt(i) == s.charAt(j+1)){ j++; } next[i] = j; @@ -632,14 +632,14 @@ class Solution { int[] next = new int[needle.length()]; getNext(next, needle); int j = -1; - for(int i = 0; i=0 && haystack.charAt(i) != needle.charAt(j+1)){ j = next[j]; } - if(haystack.charAt(i)==needle.charAt(j+1)){ + if(haystack.charAt(i) == needle.charAt(j+1)){ j++; } - if(j==needle.length()-1){ + if(j == needle.length()-1){ return (i-needle.length()+1); } } @@ -694,9 +694,9 @@ class Solution(object): :type needle: str :rtype: int """ - m,n=len(haystack),len(needle) + m, n = len(haystack), len(needle) for i in range(m): - if haystack[i:i+n]==needle: + if haystack[i:i+n] == needle: return i return -1 ``` @@ -704,31 +704,31 @@ class Solution(object): // 方法一 class Solution: def strStr(self, haystack: str, needle: str) -> int: - a=len(needle) - b=len(haystack) - if a==0: + a = len(needle) + b = len(haystack) + if a == 0: return 0 - next=self.getnext(a,needle) + next = self.getnext(a,needle) p=-1 for j in range(b): - while p>=0 and needle[p+1]!=haystack[j]: - p=next[p] - if needle[p+1]==haystack[j]: - p+=1 - if p==a-1: + while p >= 0 and needle[p+1] != haystack[j]: + p = next[p] + if needle[p+1] == haystack[j]: + p += 1 + if p == a-1: return j-a+1 return -1 def getnext(self,a,needle): - next=['' for i in range(a)] - k=-1 - next[0]=k - for i in range(1,len(needle)): - while (k>-1 and needle[k+1]!=needle[i]): - k=next[k] - if needle[k+1]==needle[i]: - k+=1 - next[i]=k + next = ['' for i in range(a)] + k = -1 + next[0] = k + for i in range(1, len(needle)): + while (k > -1 and needle[k+1] != needle[i]): + k = next[k] + if needle[k+1] == needle[i]: + k += 1 + next[i] = k return next ``` @@ -736,34 +736,34 @@ class Solution: // 方法二 class Solution: def strStr(self, haystack: str, needle: str) -> int: - a=len(needle) - b=len(haystack) - if a==0: + a = len(needle) + b = len(haystack) + if a == 0: return 0 - i=j=0 - next=self.getnext(a,needle) - while(i= 0 && s[i] != s[j+1] { - j = next[j] // 回退前一位 + j = next[j] // 回退前一位 } if s[i] == s[j+1] { j++ } - next[i] = j // next[i]是i(包括i)之前的最长相等前后缀长度 + next[i] = j // next[i]是i(包括i)之前的最长相等前后缀长度 } } func strStr(haystack string, needle string) int { @@ -796,15 +796,15 @@ func strStr(haystack string, needle string) int { } next := make([]int, len(needle)) getNext(next, needle) - j := -1 // 模式串的起始位置 next为-1 因此也为-1 + j := -1 // 模式串的起始位置 next为-1 因此也为-1 for i := 0; i < len(haystack); i++ { for j >= 0 && haystack[i] != needle[j+1] { - j = next[j] // 寻找下一个匹配点 + j = next[j] // 寻找下一个匹配点 } if haystack[i] == needle[j+1] { j++ } - if j == len(needle)-1 { // j指向了模式串的末尾 + if j == len(needle)-1 { // j指向了模式串的末尾 return i - len(needle) + 1 } } @@ -842,7 +842,7 @@ func strStr(haystack string, needle string) int { getNext(next, needle) for i := 0; i < len(haystack); i++ { for j > 0 && haystack[i] != needle[j] { - j = next[j-1] // 回退到j的前一位 + j = next[j-1] // 回退到j的前一位 } if haystack[i] == needle[j] { j++ diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md index 450d7258..b82204fa 100644 --- a/problems/0151.翻转字符串里的单词.md +++ b/problems/0151.翻转字符串里的单词.md @@ -119,7 +119,7 @@ void removeExtraSpaces(string& s) { 1. leetcode上的测试集里,字符串的长度不够长,如果足够长,性能差距会非常明显。 2. leetcode的测程序耗时不是很准确的。 -版本一的代码是比较如何一般思考过程,就是 先移除字符串钱的空格,在移除中间的,在移除后面部分。 +版本一的代码是一般的思考过程,就是 先移除字符串前的空格,再移除中间的,再移除后面部分。 不过其实还可以优化,这部分和[27.移除元素](https://programmercarl.com/0027.移除元素.html)的逻辑是一样一样的,本题是移除空格,而 27.移除元素 就是移除元素。 @@ -145,7 +145,7 @@ void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间 此时我们已经实现了removeExtraSpaces函数来移除冗余空格。 -还做实现反转字符串的功能,支持反转字符串子区间,这个实现我们分别在[344.反转字符串](https://programmercarl.com/0344.反转字符串.html)和[541.反转字符串II](https://programmercarl.com/0541.反转字符串II.html)里已经讲过了。 +还要实现反转字符串的功能,支持反转字符串子区间,这个实现我们分别在[344.反转字符串](https://programmercarl.com/0344.反转字符串.html)和[541.反转字符串II](https://programmercarl.com/0541.反转字符串II.html)里已经讲过了。 代码如下: @@ -434,49 +434,51 @@ python: ```Python class Solution: #1.去除多余的空格 - def trim_spaces(self,s): - n=len(s) - left=0 - right=n-1 + def trim_spaces(self, s): + n = len(s) + left = 0 + right = n-1 - while left<=right and s[left]==' ': #去除开头的空格 - left+=1 - while left<=right and s[right]==' ': #去除结尾的空格 - right=right-1 - tmp=[] - while left<=right: #去除单词中间多余的空格 - if s[left]!=' ': + while left <= right and s[left] == ' ': #去除开头的空格 + left += 1 + while left <= right and s[right] == ' ': #去除结尾的空格 + right = right-1 + tmp = [] + while left <= right: #去除单词中间多余的空格 + if s[left] != ' ': tmp.append(s[left]) - elif tmp[-1]!=' ': #当前位置是空格,但是相邻的上一个位置不是空格,则该空格是合理的 + elif tmp[-1] != ' ': #当前位置是空格,但是相邻的上一个位置不是空格,则该空格是合理的 tmp.append(s[left]) - left+=1 + left += 1 return tmp -#2.翻转字符数组 - def reverse_string(self,nums,left,right): - while left len ? len : i + k; while(++l < --r) [resArr[l], resArr[r]] = [resArr[r], resArr[l]]; } diff --git a/problems/剑指Offer05.替换空格.md b/problems/剑指Offer05.替换空格.md index 53f0315f..ab6eedf7 100644 --- a/problems/剑指Offer05.替换空格.md +++ b/problems/剑指Offer05.替换空格.md @@ -36,7 +36,7 @@ i指向新长度的末尾,j指向旧长度的末尾。 这么做有两个好处: 1. 不用申请新数组。 -2. 从后向前填充元素,避免了从前先后填充元素要来的 每次添加元素都要将添加元素之后的所有元素向后移动。 +2. 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。 时间复杂度,空间复杂度均超过100%的用户。 diff --git a/problems/剑指Offer58-II.左旋转字符串.md b/problems/剑指Offer58-II.左旋转字符串.md index c34b2a71..1b619ffb 100644 --- a/problems/剑指Offer58-II.左旋转字符串.md +++ b/problems/剑指Offer58-II.左旋转字符串.md @@ -31,7 +31,7 @@ 不能使用额外空间的话,模拟在本串操作要实现左旋转字符串的功能还是有点困难的。 -那么我们可以想一下上一题目[字符串:花式反转还不够!](https://programmercarl.com/0151.翻转字符串里的单词.html)中讲过,使用整体反转+局部反转就可以实现,反转单词顺序的目的。 +那么我们可以想一下上一题目[字符串:花式反转还不够!](https://programmercarl.com/0151.翻转字符串里的单词.html)中讲过,使用整体反转+局部反转就可以实现反转单词顺序的目的。 这道题目也非常类似,依然可以通过局部反转+整体反转 达到左旋转的目的。 @@ -41,7 +41,7 @@ 2. 反转区间为n到末尾的子串 3. 反转整个字符串 -最后就可以得到左旋n的目的,而不用定义新的字符串,完全在本串上操作。 +最后就可以达到左旋n的目的,而不用定义新的字符串,完全在本串上操作。 例如 :示例1中 输入:字符串abcdefg,n=2 @@ -75,7 +75,7 @@ public: 在这篇文章[344.反转字符串](https://programmercarl.com/0344.反转字符串.html),第一次讲到反转一个字符串应该怎么做,使用了双指针法。 -然后发现[541. 反转字符串II](https://programmercarl.com/0541.反转字符串II.html),这里开始给反转加上了一些条件,当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。 +然后发现[541. 反转字符串II](https://programmercarl.com/0541.反转字符串II.html),这里开始给反转加上了一些条件,当需要固定规律一段一段去处理字符串的时候,要想想在for循环的表达式上做做文章。 后来在[151.翻转字符串里的单词](https://programmercarl.com/0151.翻转字符串里的单词.html)中,要对一句话里的单词顺序进行反转,发现先整体反转再局部反转 是一个很妙的思路。