目录
讀題
28. 实现 strStr()
自己看到题目的第一想法
看到的第一個想法是使用滑動窗口的方法,從零開始,假設第一個字符與needle字符相等,則進入迴圈,假設迴圈當中有不符合的狀況,跳出迴圈,如果一致return i ,都不一致則return -1;
看完代码随想录之后的想法
初見KMP算法,還有很多細節要著墨的,但今天在思考的過程中,原本m * n 的解法改為,主字串只要走一遍,至於比較的部分我利用子字符串他的規律來進行跳動
比如說今天要找aafaae這個子字串,我們可以觀察到,aa是相同的部分
那在aafaafaae我要在裡面找到這個子字串
如果用原本的方法,我走到aafaa”f” 這個f因為跟我的子字串的”e”不相符,所以我的i會跳回去主字串的第二個a,再依序往後比對
但假設使用KMP 我知道aafaae他所組成的next數組是 [ -1, 0, -1, 0, 1, -1],假設我比對到aafaa”f”時我可以利用這個數組跳到上一個aa在的地方,也就是a”a”faae的ˋ第二個a的地方之後在+1比對兩個字串目前的字元是否一致。
目前講得還是會比較攏統,但是以這個想法來寫,效率真的提高蠻多的,需要再花點時間琢磨琢磨
459.重复的子字符串
自己看到题目的第一想法
看到這個題目的提示是使用KMP法,我聯想到建立NEXT數組的時候,我們會對於整體去做J要如何移動的依據,嘗試進行畫圖會發現,如果是重複的,並且有發現到一個可能的規律是在於,是由重複的子字符串進行比對,如果以兩個字幅串比對,並且每組都是九個為一組,納第二組的j最後就會++到八,感覺有點聯繫,但實在想不出來要怎麼解
看完代码随想录之后的想法
看完之後,比較快可以理解,透過減去最長相等的後輟的特性,我們可以知道除了第一組之外的長度,也就是後續不斷++的長度,假設next最後一個數是八 整個字串長度是18 那我們可以知道 最長後輟長度是九 整個字串長度減去最長後輟的長度,就會等於第一組的長度,假設這個長度能夠整除18%9 餘零,代表這是一個長度為九,重複兩次的字串
28. 实现 strStr() - 實作
思路
m * n 思路
- 設定一個need = 0 → 用來存放起始位置
- 進入迴圈,逐步往前 → 因為起始點有可能在第二個,
- 假設i 跟子字符串的起始點相同,則進入判斷式
- need = i; → 存放起始位置
- 進入迴圈,假設i < haystack.size && j < needle.size → 避免操作到空數組
- 如果haystack[i] == needle[j] 則i, j++
- 如果不同 i回到起始位置,j返回0 ,跳出迴圈
- 假設j == needle.size() 代表包含子字符串,回傳need
- 假設i 跟子字符串的起始點相同,則進入判斷式
- 迴圈結束,都沒有子字符串相等,回傳 -1;
m + n 思路
- 為needle字串建立一個next的數組,用來標記j 要怎麼跳
- 設定j = -1;
- for迴圈,遍歷haystack字串
- 假設haystack[i] ≠ needle[j + 1] 並且j >= 0 → j + 1 的原因是因為j 等於-1 j ≥ 0 才需要回跳,不然j+1 = 0 needle會保持在0的位置
- j = needle[j] → 跳到與之前相符的位置 也就是aafaa 從第二組aa的第一個a 跳到第一組aa的第二個a
- 假設相等 j++;
- if ( j == (t.size - 1) { return i - j; }
- else return -1
- 假設haystack[i] ≠ needle[j + 1] 並且j >= 0 → j + 1 的原因是因為j 等於-1 j ≥ 0 才需要回跳,不然j+1 = 0 needle會保持在0的位置
Code
m * n
class Solution {
public:
int strStr(string haystack, string needle) {
int need = 0;
int j = 0;
for(int i = 0; i < haystack.size(); i++) {
if(haystack[i] == needle[j]) {
need = i;
while( i < haystack.size() && j < needle.size()) {
if(haystack[i] == needle[j]) {
i++;
j++;
} else {
i = need;
j = 0;
break;
}
if(j == needle.size()){
return need;
}
}
}
}
return -1;
}
};
m+n
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.size() == 0){
return 0;
}
int next[needle.size()];
int j = -1, i = 0;
next[0] = j;
for(i = 1; i < needle.size(); i++){
while(j >= 0 && needle[i] != needle[j + 1]) {
j = next[j];
}
if(needle[i] == needle[j + 1]) { // 因為也有可能不等於
j++;
}
next[i] = j;
}
j = -1;
for(i = 0; i < haystack.size(); i++) {
while(j >= 0 && haystack[i] != needle[j + 1]) {
j = next[j];
}
if(haystack[i] == needle[j + 1]) {
j++;
}
if(j == needle.size() - 1) {
return i - j;
}
}
return -1;
}
};
459.重复的子字符串 - 實作
思路
- 將字串轉換成next數組,並且取最後一個數
- 最後一個數+1 → 取得最長後輟長度
- 整個字符串長度 - 最長後輟長度 = 最小前輟長度
- 整個字符串長度 % 最小前輟長度 == 0 代表重複,不等於代表不重複
Code
class Solution {
public:
bool repeatedSubstringPattern(string s) {
int next[s.size()];
int j = -1;
next[0] = j;
for( int i = 1; i < s.size(); i++){
while ( j >= 0 && s[i] != s[j + 1]) {
j = next[j];
}
if(s[i] == s[j + 1]) {
j++;
}
next[i] = j;
}
if(next[s.size() - 1] != -1 && s.size() % (s.size() - (next[s.size() - 1] + 1)) == 0) {
return true;
}
return false;
}
};
字符串總結筆記
庫函數的使用
在字符串的學習當中,因為之前都是碰C,所以對於C++的庫函數都很不熟悉,比如最基礎的swap以及reverse都不太熟悉,但是正因為這個事情,所以我比較少去依賴庫函數,常常會想自己做,不過這個過程中,也是真的很有趣,比如說反轉字串,我學到了還可以用位運算進行字串反轉,真的是驚到我了
雙指針法
在學習反轉字串的時候,雙指針法真的很好用,其實廣義來說KMP也是用到兩個指針,來進行變換,讓我了解到,不是有越多工具越好,有時可以把一項精典工具用精,就可以很容易的去處理
花式反轉
在反轉字符串ll 以及翻轉字符串的單詞,以及左旋轉字符串,都讓我感到很好玩,原來程式可以這麼花式,並且我在跟其他群組的小夥伴討論左旋轉字符串時,我還了解到這個如果要深究還可以牽涉到某個數論,真的非常有趣
KMP
我不敢說我自己已經了解了,在學習KMP的過程當中,其實碰到很多困難,但就是靜下心來去畫圖,重複看好幾片影片以及小夥伴提供的資料去輔助,我感覺到後面寫起來是知道為甚麼這一段CODE要這麼寫,而不是照著模板,這樣的學習真的很讓人滿足
總結
字符串就像卡哥說的,想法很簡單,但實現起來其實有很多眉眉角角要進行處理,雙指針法好好用,KMP法要想辦法更加了解其精隨,學習字符串的過程中,感覺到最多的不是困難,而是有趣,尤其是自己想盡辦法思考出來的思路,假設有一點點跟提解接近,那就會很開心了。
雙指針回顧筆記
數組
不論是二分法、滑動窗口、快慢指針,就是根據不同題目的調性,去靈活運用兩個指針彼此的關係,回到第一點,只要定義清楚了,其實雙指針的做法很快就可以根據自己的思路進行設計。
字符串
字符串在反轉字串、以及KMP都有用兩個指針在那進行交換
並且在反轉字串時,雙指針可以針對不同的需求去做應用,比如說只反轉前K個就可以讓指針去自己想定位的位置
鏈表
雙指針非常重要,不論是206.反转链表還是142.环形链表II,都是解題的關鍵
尤其是環形鏈表ll,使用快慢指針,並且這道題目還可以用數學的方式去理解,真的很好玩
N數之和
最後就是15.三數之和以及18.四數之和,這兩道題目我看著我當初的思路,真的沒有想到就是利用雙指標來解決這道題目,
就算卡哥有提示可以我也沒有想得很明白,直到看完卡哥兩題的解題思路,我突然豁然開朗
15.三數之和重點就是四個
- 排序 → 為了雙指針的變動以及去重的方便
- 第一個數位置要放哪
- 雙指針的範圍要怎麼設定
- 以及如何去重
18.四數之和重點多一個
- 排序 → 為了雙指針的變動以及去重的方便
- target有可能是負數,要記得在剪枝時要進行判斷
- 第一個數以及第二個數位置要放哪
- 雙指針的範圍要怎麼設定
- 以及如何去重
在雙指針的寫法當中,我在這種左右邊界的雙指針,我學到了要記得排序,不然這個雙指針會沒辦法使用,而我一開始沒有想到的就是這點,想到這點之後就比較通透了。
總結
對於雙指針法我不能說熟練,只能說目前就是了解,以及稍微會使用了,感覺這個方法後續還會一直碰到,找個時間再來複習。
總結
自己实现过程中遇到哪些困难
KMP算法真的理解很久,但目前掌握到一點皮毛了,後續會對於這個部分在進行更深入的了解
今日收获,记录一下自己的学习时长
今日大概學了3、4HR 左右,工作比較忙,但了解了一點KMP的概念後,真的很開心。
相關資料
- 实现 strStr()
题目链接/文章讲解/视频讲解:https://programmercarl.com/0028.实现strStr.html
459.重复的子字符串 (本题可以跳过)
题目链接/文章讲解/视频讲解:https://programmercarl.com/0459.重复的子字符串.html
字符串总结
题目链接/文章讲解:https://programmercarl.com/字符串总结.html
双指针回顾