基础知识
字符串由任意长度的字符组成,是编程语言中表示文本的数据类型。String类型所表达的字符串是无法改变的,也就是说,只能对字符串进行读操作。如果对字符串进行写操作,那么修改的内容在返回值的字符串中,原来的字符串保持不变。
算法题目
一、字符串中的变位词
题目:输入字符串s1和s2,如何判断字符串s2中是否包含字符串s1的某个变位词?如果字符串s2中包含字符串s1的某个变位词,则字符串s1至少有一个变位词是字符串s2的子字符串。假设两个字符串中只包含英文小写字母。例如,字符串s1为"ac",字符串s2为"dgcaf",由于字符串s2中包含字符串s1的变位词"ca",因此输出为true。如果字符串s1为"ab",字符串s2为"dgcaf",则输出为false。
解题思路: 变位词具有以下几个特点。首先,一组变位词的长度一定相同;其次,组成变位词的字母集合一定相同,并且每个字母出现的次数也相同。 如果一个哈希表的键是字母,而哈希表中的值是对应字母出现的次数,那么这样一个哈希表很适合用来统计字符串中每个字母出现的次数。 将s1出现字符的情况记录在哈希表中。然后按字符s1长度差距的两个指针,在s2字符串上移动,每移动一个,减去哈希表对应的字符键的值。然后遍历哈希表,如果哈希表中全部为0时,就说明找到了变位词。
Golang代码:
func checkInclusion(s1 string, s2 string) bool {
if len(s1) > len(s2) {
return false
}
memo := make(map[uint8]int)
for i := uint8('a'); i < uint8('a') + 26; i++ {
memo[i] = 0
}
for i := 0; i < len(s1); i++ {
memo[s1[i]]++
}
var (
left = 0
right = len(s1) - 1
)
for j := 0; j < len(s2); j++ {
if j < len(s1) {
memo[s2[j]]--
} else {
memo[s2[left]]++
left++
right++
memo[s2[right]]--
}
if allZero(memo) {
return true
}
}
return false
}
func allZero(data map[uint8]int) bool {
for _, item := range data {
if item != 0 {
return false
}
}
return true
}
二、不含重复字符的最长子字符串
题目:输入一个字符串,求该字符串中不含重复字符的最长子字符串的长度。例如,输入字符串"babcca",其最长的不含重复字符的子字符串是"abc",长度为3。
解题思路: 和前面的题目一样,此处还是用一个哈希表统计子字符串中字符出现的次数。如果一个子字符串中不含重复的字符,那么每个字符都只出现一次,它们在哈希表中对应的值为1。没有在子字符串中出现的其他字符对应的值都是0。也就是说,如果子字符串中不含重复字符,那么它对应的哈希表中没有比1大的值。
func lengthOfLongestSubstring(s string) int {
memo := make(map[uint8]int)
for i := uint8('a'); i < uint8('a') + 26; i++ {
memo[i] = 0
}
var(
maxLen, sLen, left int
strMap = make(map[uint8]int)
)
for i := 0; i < len(s); i++ {
memo[s[i]]++
if allOne(memo) {
sLen++
if sLen > maxLen {
maxLen = sLen
}
strMap[s[i]] = i
} else {
dealMemo(memo, s[left:strMap[s[i]] + 1])
left = strMap[s[i]] + 1
sLen = i - left + 1
strMap[s[i]] = i
}
}
return maxLen
}
func dealMemo(memo map[uint8]int, s string) {
for i := 0; i < len(s); i++ {
memo[s[i]]--
}
}
func allOne(data map[uint8]int) bool {
for _, item := range data {
if item > 1 {
return false
}
}
return true
}
三、最多删除一个字符得到回文
给定一个非空字符串
s,请判断如果 最多 从字符串中删除一个字符能否得到一个回文字符串。
解题思路: 双指针,头部指针P1向右移动,尾部指针P2向左移动,进行对比。如果碰到不相等的字符,就P1向右移动一位,再进行回文对比,或P2向左移动一位,再进行回文,如果有一种是回文,就表示成功。
Golang代码:
func validPalindrome(s string) bool {
var (
left int
right = len(s) - 1
)
for left <= len(s)/2 {
if s[left] == s[right] {
left++
right--
} else {
return palindrome(s[left+1:right+1]) || palindrome(s[left:right])
}
}
return true
}
func palindrome(s string) bool {
var (
left int
right = len(s) - 1
)
for left < right {
if s[left] != s[right] {
return false
}
left++
right--
}
return true
}
四、回文子字符串的个数
给定一个字符串
s,请计算这个字符串中有多少个回文子字符串。具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
解题思路: 判断回文的方式有两种:一种是从两头向中心移动对比字符,另一种是从中间向两头移动对比字符。本题就是用第二种方式。从头到尾遍历字符串,以每个字符为中心,向两边移动对比。也可能是偶数回文,就以当前和后一个字符为中心,向两边发散对比。每对比成功一次就count++,得到最终count。
Golang代码:
func countSubstrings(s string) int {
var count int
for i := 0; i < len(s); i++ {
spreadFromCentre(s, i, &count)
}
return count
}
func spreadFromCentre(s string, i int, count *int) {
var (
left = i
right = i
)
for left >= 0 && right < len(s) {
if s[left] == s[right] {
*count++
left--
right++
} else {
break
}
}
left = i
right = i
right++
if right < len(s) && s[left] == s[right] {
for left >= 0 && right < len(s) {
if s[left] == s[right] {
*count++
left--
right++
} else {
break
}
}
}
return
}