難度:簡單
給定 S 和 T 兩個字串,當它們分別被輸入到空白的文字編輯器後,判斷二者是否相等,並返回結果。 # 代表退格字元。
注意:如果對空文字輸入退格字元,文字繼續為空。
示例 1:
輸入:S = “ab#c”, T = “ad#c”
輸出:true
解釋:S 和 T 都會變成 “ac”。
示例 2:
輸入:S = “ab##”, T = “c#d#”
輸出:true
解釋:S 和 T 都會變成 “”。
示例 3:
輸入:S = “a##c”, T = “#a#c”
輸出:true
解釋:S 和 T 都會變成 “c”。
示例 4:
輸入:S = “a#c”, T = “b”
輸出:false
解釋:S 會變成 “c”,但 T 仍然是 “b”。
提示:
- <= S.length <= 200
- <= T.length <= 200
- S 和 T 只含有小寫字母以及字元 ‘#’。
進階:
你可以用 O(N) 的時間複雜度和 O(1) 的空間複雜度解決該問題嗎?
來源:力扣(LeetCode)
連結:leetcode-cn.com/problems/backspace...
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
簡單進行一下分析
也就是說, 如果符號有一個 #
,我們就要刪除一個字元.
兩個就刪除兩個.
那我們只能 遍歷字元.
這就是方法一,最簡單的.
第二: 官方解析
2.1:方法一: 重構字串
思路以及演算法
最容易想到的方法是將給定的字串中的退格符和應當被刪除的字元都去除,還原給定字串的一般形式。然後直接比較兩字串是否相等即可。
具體地,我們用棧處理遍歷過程,每次我們遍歷到一個字元:
- 如果它是退格符,那麼我們將棧頂彈出;
- 如果它是普通字元,那麼我們將其壓入棧中。
func build(str string) string {
// 分配儲存 陣列的記憶體空間,s結構體的array指標指向這個陣列
s := []byte{} // 知識點1,看下文
for i := range str {
// 遍歷判斷 如果不存在 #
if str[i] != '#' {
// 把當前的字元 壓入s 陣列中
fmt.Println("來了沒有有#")
s = append(s, str[i])
fmt.Println(s)
} else if len(s) > 0 {
// 如果我的s 有字元 (隱藏條件,這裡沒有特殊寫出,並且 沒有 # ) 則 直接 讓我的 s 長度減去1
fmt.Println("來了有#")
fmt.Println(s)
s = s[:len(s)-1] // 擷取 到最後一位. 最後一位不要了. 因為我們這裡是有# 所以需要刪除最後一位呀
fmt.Println("去掉最後進去的")
fmt.Println(s)
}
}
return string(s)
}
// 這個方法是leetcode 給出的測試用例,邏輯先走這裡
func backspaceCompare(s, t string) bool {
return build(s) == build(t)
}
方法一的複雜度分析
時間複雜度:O(N+M)O(N+M),其中 NN 和 MM 分別為字串 SS 和 TT 的長度。我們需要遍歷兩字串各一次。
空間複雜度:O(N+M)O(N+M),其中 NN 和 MM 分別為字串 SS 和 TT 的長度。主要為還原出的字串的開銷。
2.2:方法二:雙指標
思路及演算法
一個字元是否會被刪掉,只取決於該字元後面的退格符,而與該字元前面的退格符無關。因此當我們逆序地遍歷字串,就可以立即確定當前字元是否會被刪掉。
具體地,我們定義 skip
表示當前待刪除的字元的數量。每次我們遍歷到一個字元:
若該字元為退格符,則我們需要多刪除一個普通字元,我們讓
skip
加 1;若該字元為普通字元:
- 若
skip
為 0,則說明當前字元不需要刪去; - 若
skip
不為 0,則說明當前字元需要刪去,我們讓skip
減 1。
- 若
這樣,我們定義兩個指標,分別指向兩字串的末尾。每次我們讓兩指標逆序地遍歷兩字串,直到兩字串能夠各自確定一個字元,然後將這兩個字元進行比較。重複這一過程直到找到的兩個字元不相等,或遍歷完字串為止。
#
// 函式名字為 leetcode 提前建立好的測試用例
func backspaceCompare(s, t string) bool {
// 建立兩個空指標
skipS, skipT := 0, 0
// 從傳入的s t末尾開始,長度限制為 當前長度 減去1
i, j := len(s)-1, len(t)-1
// 必須有一個是有 位元組的 才進入迴圈
for i >= 0 || j >= 0 {
for i >= 0 {
// 遍歷 s 如果 s 中有 # 讓 skip指標 +1
if s[i] == '#' {
skipS++
i--
// 如果 我的指標 大於0 (隱藏條件,這裡沒有特殊寫出,並且 沒有 # )
} else if skipS > 0 {
// 就指標後退一個
skipS--
// 迴圈i 正常減去1
i--
// 其他情況, 就跳出迴圈
} else {
break
}
}
// 此處原理與上相同
for j >= 0 {
if t[j] == '#' {
skipT++
j--
} else if skipT > 0 {
skipT--
j--
} else {
break
}
}
// 如果 i 或者j 還有位元組.
if i >= 0 && j >= 0 {
// 對比 如果 s 與 t 中當前迴圈 指標 對不上,說明以後也對不上,直接返回false
if s[i] != t[j] {
return false
}
// 如果 有一個還有位元組,另外一個沒位元組了. 這裡寫法也可以反過來寫一樣的
// 說明後面也對不上,後面哪怕是 # 或者 任意字元,都不一樣了.
// 直接返回false
} else if i >= 0 || j >= 0 {
return false
}
// 什麼都沒有,說明觸發到0 了. 也執行 減1 讓他接下來結束最後一次迴圈
i--
j--
}
return true
}
方法二的 複雜度分析
時間複雜度:O(N+M)O(N+M),其中 NN 和 MM 分別為字串 SS 和 TT 的長度。我們需要遍歷兩字串各一次。
空間複雜度:O(1)O(1)。對於每個字串,我們只需要定義一個指標和一個計數器即可。
1.0 str與byte如何取捨?
s := []byte{}
既然string就是一系列位元組,而[]byte也可以表達一系列位元組,那麼實際運用中應當如何取捨?
- string可以直接比較,而[]byte不可以,所以[]byte不可以當map的key值。
- 因為無法修改string中的某個字元,需要粒度小到操作一個字元時,用[]byte。
- string值不可為nil,所以如果你想要通過返回nil表達額外的含義,就用[]byte。
- []byte切片這麼靈活,想要用切片的特性就用[]byte。
- 需要大量字串處理的時候用[]byte,效能好很多。
在本文程式碼我們需要操作一個字元,所以我們用 []byte
2.0 golang的 := 宣告變數
:=
這個符號直接取代了var
和type
,這種形式叫做簡短宣告
。\不過它有一個限制,那就是它只能用在函式內部;在函式外部使用則會無法編譯通過,所以一般用var
方式來定義全域性變數
**。
換句話說,“:=”只能在宣告“區域性變數”的時候使用,而“var”沒有這個限制。
我們來看看下面三種方式定義變數,很明顯 第三行是最簡單的. 但是要注意區域性變數的問題哈!
var number1, number2, number3 int = 1, 2, 3
var number1, number2, number3 = 1, 2, 3
number1, number2, number3 := 1, 2, 3
本作品採用《CC 協議》,轉載必須註明作者和本文連結