目录
1. 反转字符串(344)
【题目描述】
将一个字符串反转,要求空间复杂度为 O(1) 。
【示例】
Input: s = ["h","e","l","l","o"]
Output: ["o","l","l","e","h"]
【思路】
双指针法:
定义两个指针(也可以说是索引下标),一个指向字符串起始位置,另一个指向字符串结尾位置,两个指针同时向中间移动,并交换元素。
【Go代码】
func reverseString(s []byte) {
left, right := 0, len(s)-1
for left < right {
s[left], s[right] = s[right], s[left]
left++
right--
}
}
// 简洁版
func reverseString(s []byte) {
for i, j := 0, len(s) - 1; i < j; i, j = i + 1, j - 1 {
s[i], s[j] = s[j], s[i]
}
}
【性能分析】
时间复杂度:O(n)
空间复杂度:O(1)
2. 反转字符串II(541)
【题目描述】
分段反转字符串,在字符串中,从前向后遍历,每 2k 个字符的前 k 个字符需要反转。经过上述操作之后,对于字符串最后剩下的不足 2k 个的字符,此时如果字符小于 k 个,则反转剩下的所有字符,如果剩下的字符小于 2k 但大于或等于 k 个,则反转前 k 个字符。
【示例1】
Input: s = "abcdefg", k = 2
Output: "bacdfeg"
【示例2】
Input: s = "abcd", k = 2
Output: "bacd"
【思路】
题目中要找的是每个 2k 区间的起点,在使用 for 遍历字符串的过程中,只要让 i+=2*k ,i 每次移动 2k ,然后判断是否有需要反转的区间即可。
【Go代码】
func reverseStr(s string, k int) string {
res := []byte(s)
length := len(s)
for i := 0; i < length; i += 2 * k {
if i + k <= length {
reverse(res[i:i+k])
} else {
reverse(res[i:length])
}
}
return string(res)
}
func reverse(a []byte) {
for i, j := 0, len(a) - 1; i < j; i, j = i + 1, j - 1 {
a[i], a[j] = a[j], a[i]
}
}
【性能分析】
时间复杂度:O(n)
空间复杂度:O(n)
3. 反转字符串里的单词(151)
【题目描述】
给定一句英文字符串 s ,颠倒单词的顺序。一个完整的单词中间不包含空格。s 中的单词将被至少一个空格隔开。返回一个由单个空格按相反顺序连接的字符串。注意,给定的 s 前后可能有空格,两个单词之间可能有多个空格。返回的字符串应该只有一个空格来分隔单词且前后没有空格。不可以使用额外的辅助空间。
【示例1】
Input: s = "the sky is blue"
Output: "blue is sky the"
【示例2】
Input: s = " hello world "
Output: "world hello"
Explanation: Your reversed string should not contain leading or trailing spaces.
【示例3】
Input: s = "a good example"
Output: "example good a"
Explanation: You need to reduce multiple spaces between two words to a single space in the reversed string.
【思路】
- 删除冗余空格
- 反转整个字符串
- 反转每个单词
【Go代码】
func reverseWords(s string) string {
// 1.删除冗余空格
res := removeExtraSpaces(s)
// 2.反转整个字符串
reverse(&res, 0, len(res) - 1)
// 3.反转每个单词
var start, end int // 记录每个单词的起止位置
for i := 0; i < len(res); i++ {
// 单词后面有空格
if res[i] == ' ' {
end = i - 1
reverse(&res, start, end)
start = i + 1
}
// 最后一个单词之后没有空格
if i == len(res) - 1 {
end = i
reverse(&res, start, end)
}
}
return string(res)
}
func removeExtraSpaces(s string) []byte {
slow, fast := 0, 0
res := []byte(s)
// 删除头部空格
for len(res) > 0 && fast < len(res) && res[fast] == ' ' {
fast++
}
// 删除单词间冗余空格
for ; fast < len(res); fast++ {
if fast - 1 > 0 && res[fast] == ' ' && res[fast - 1] == res[fast] {
continue
} else {
res[slow] = res[fast]
slow++
}
}
// 删除尾部空格
if slow - 1 > 0 && res[slow - 1] == ' ' {
res = res[:slow-1]
} else {
res = res[:slow]
}
return res
}
func reverse(s *[]byte, start, end int){
for i, j := start, end; i < j; i, j = i + 1, j - 1 {
(*s)[i], (*s)[j] = (*s)[j], (*s)[i]
}
}
【性能分析】
时间复杂度:O(n)
空间复杂度:O(n)
4. 实现strStr()(28)
【题目描述】
在文本串 s 中查找是否出现过模式串 t ,如果出现过则返回匹配的第一个位置,如果没有则返回 -1 。
【示例1】
Input: haystack = "hello", needle = "ll"
Output: 2
【示例2】
Input: haystack = "aaaaa", needle = "bba"
Output: -1
【示例3】
Input: haystack = "", needle = ""
Output: 0
【思路】
KMP算法。
【Go代码】
func strStr(haystack string, needle string) int {
length := len(needle)
if length == 0 {
return 0
}
j := 0
next := make([]int, length)
getNext(next, needle)
for i := 0; i < len(haystack); i++ {
for j > 0 && haystack[i] != needle[j] {
j = next[j - 1]
}
if haystack[i] == needle[j] {
j++
}
if j == length {
return i - length + 1
}
}
return -1
}
func getNext(next []int, s string){
// i:后缀末尾,j:前缀末尾(i之前包括i在内的最长相等前后缀的长度)
// 1.初始化
j := 0
next[0] = 0
for i := 1; i < len(s); i++ {
// 2.前后缀不相同
for j > 0 && s[i] != s[j] {
j = next[j - 1]
}
// 3.前后缀相同
if s[i] == s[j] {
j++
}
// 4.给 next 数组赋值
next[i] = j
}
}
【性能分析】
时间复杂度:O( len(s)+len(t) )
空间复杂度:O( len(t) )
5. 重复的子字符串(459)
【题目描述】
给出一个字符串,判断该字符串是否由重复子字符串构成。
【示例1】
Input: s = "abcabcabcabc"
Output: true
Explanation: It is the substring "abc" four times or the substring "abcabc" twice.
【示例2】
Input: s = "aba"
Output: false
【思路】
KMP算法的 next 数组记录的是最长相等前后缀的长度,如果 next[len-1]!=0 ,说明字符串有相同的前后缀。最长相等前后缀的长度为 next[len-1] ,数组长度为 len 。如果 len%(len-next[len-1])==0 ,说明(数组长度 - 最长相等前后缀的长度)正好可以被数组的长度整除,该字符串由重复的子字符串组成。数组长度减去最长相等前后缀的长度相当于第一个重复子字符串的长度,也就是一个重复周期的长度,如果这个周期可以被整除,说明整个数组就是这个周期的循环。
【Go代码】
func repeatedSubstringPattern(s string) bool {
n := len(s)
if n == 0 {
return false
}
// 1.求next数组
j := 0
next := make([]int, n)
next[0] = 0
for i := 1; i < n; i++ {
for j > 0 && s[i] != s[j] {
j = next[j - 1]
}
if s[i] == s[j] {
j++
}
next[i] = j
}
// 2.判断
if next[n - 1] != 0 && n % (n - next[n - 1]) == 0 {
return true
} else {
return false
}
}
【性能分析】
时间复杂度:O(n)
空间复杂度:O(n)