===问:
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:
输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
===答:
方法一:
执行用时 :0 ms,击败了100.00% 的用户
内存消耗 :2.4 MB, 击败了27.91%的用户
以字符串长度最短的为参考源,依次比对找到不匹配的跳出,比较符合本人一般的思维模式
func longestCommonPrefix1(strs []string) string {
s := ""
l := 0
j := 0
n := 0
m := len(strs)
switch m {
case 0:
return ""
case 1:
return strs[0]
}
// 找到字符最少的字符串
for i, v := range strs {
// 如果有一个字符串为空,则跳出
if len(v) == 0 {
return ""
}
// 选择最小字符数的
if len(v) < l || l == 0 {
l = len(v)
j = i
}
}
// 以字符最少的字符串进行循环
for i, v := range strs[j] {
// 遍历所有字符串
for _, y := range strs {
//如果字符相同,则+1
if string(v) == string(y[i]) {
n++
}
}
// 如果n的数量和strs的长度相同,则表示都存在该字符,继续循环,否则跳出并返回s
if n == len(strs) {
s += string(v)
n = 0
} else {
return s
}
}
return s
}
方法二:
执行用时 :0 ms, 击败了100.00% 的用户
内存消耗 :2.4 MB, 击败了89.71%的用户
任意一个元素都可以作为参考源,而不是方法一中必须使用字符串长度最短的那个作为参考源。
效率差不多,但是内存消耗似乎稍稍降低了一点点。
func longestCommonPrefix2(strs []string) string {
// 先排除掉一些
l := len(strs)
switch l {
// strs为空
case 0:
return ""
// strs仅一个元素
case 1:
return strs[0]
}
// 设定第一个元素作为前缀的参考源
p := strs[0]
// 循环时不含参考源(第一个参考源),少一次比较
for _, x := range strs[1:] {
// 本次元素的长度,内层循环多次需要用到
l = len(x)
// 参考源(第一个元素)的各个字符 依次匹配 本元素对应位置的字符,
// 一发现有不匹配的就跳出,并修改参考源
for i := range p {
//如果该字符串长度比i的值小了,则表示该字符串已经到头,这样p就停止在本位置
if i >= l {
// p截取最新的,并且跳出循环,下一次循环使用最新的p
p = p[:l]
break
}
// 如果同位置字符与参考源同位置字符不同,则修改参考源
if x[i] != p[i] {
// p截取最新的,并且跳出循环,下一次循环使用最新的p
p = p[:i]
// 这里必须跳出终止本次循环,回到外层循环,找到strs的下一个元素再进行比对
break
}
}
}
return p
}
===解:
本题在算法层面没什么难度,就是按照一般的思路走就行了,方法二比方法一升级一点。
本题反倒让我在语言层面有所学习:
go语言中字符串默认情况下就是个[]uint8
(别名:[]byte
)的切片,因此可以直接用切片的方式对其进行切割,而输出的时候又是正常的字符串,例题如下。
s := "a测,123试"
fmt.Println(s[:1]) //输出:a,表明英文占1位
fmt.Println(s[1:4]) //输出:测,表明中文占3位
fmt.Println(s[4:7]) //输出:,,表明中文符号占3位
fmt.Println(s[7:10]) //输出:123,表明数字占1位
/* 输出
a
测
,
123
*/
如果使用默认方式,那么当遇到中文等字符的时候,就会占用三个位置,这时使用不当就可能出现乱码。那么为了防止出错,我们可以提前将字符串切片改为[]int32
(别名[]rune
)形式,输出时再利用string()
变回字符串,例题如下。
t := []int32("a测,123试")
fmt.Println(t[0], string(t[0])) // 英文占一位
fmt.Println(t[1], string(t[1])) // 中文占1位
fmt.Println(t[2], string(t[2])) // 中文符号占1位
fmt.Println(t[3:6], string(t[3:6])) //数字占1位
// 97 a
// 27979 测
// 65292 ,
// [49 50 51] 123
[]uint8
(别名[]byte
)模式下获得的内容可以直接输出,比如fmt.Println(s[7:10])
[]int32
(别名[]rune
)模式下获得的内容输出后为int32
类型的数值
使用for循环遍历字符串的时候,直接取值v
的类型为int32
(别名rune
),通过索引i
取值的类型为uint8
(别名byte
)。在有中文情况下,i
的值可能会有间隔
r := "a测,123试"
for i, v := range r {
fmt.Printf("%v、v类型:%T,v值:%v;r[i]类型:%T,r[i]值:%v\n", i, v, v, r[i], r[i])
}
// 0、v类型:int32,v值:97;r[i]类型:uint8,r[i]值:97
// 1、v类型:int32,v值:27979;r[i]类型:uint8,r[i]值:230
// 4、v类型:int32,v值:65292;r[i]类型:uint8,r[i]值:239
// 7、v类型:int32,v值:49;r[i]类型:uint8,r[i]值:49
// 8、v类型:int32,v值:50;r[i]类型:uint8,r[i]值:50
// 9、v类型:int32,v值:51;r[i]类型:uint8,r[i]值:51
// 10、v类型:int32,v值:35797;r[i]类型:uint8,r[i]值:232
单引号包含的内容是字符,其值为int32
(别名rune
)
fmt.Printf("类型:%T,值:%v\n", 'a', 'a')
fmt.Printf("类型:%T,值:%v\n", '测', '测')
fmt.Printf("类型:%T,值:%v\n", ',', ',')
fmt.Printf("类型:%T,值:%v\n", '1', '1')
// 类型:int32,值:97
// 类型:int32,值:27979
// 类型:int32,值:65292
// 类型:int32,值:49
上面说过切片可以直接输出,但前提是这个字符串已经存在,如果起始值是数值,或数值型切片,那就必须要使用string()
来解析成字符串再输出。
// 整数
fmt.Println(int32(27979)) //输出:27979
fmt.Println(string(27979)) //输出:测
fmt.Println(string(int32(27979))) //输出:测
// 切片
u := "测"
fmt.Println(u[0:3]) //输出:测
fmt.Println([]int32{27979}) //输出:[27979]
fmt.Println(string([]int32{27979})) //输出:测
fmt.Println(string([]int32{27979, 27979})) //输出:测测
fmt.Println(string([]uint8{230, 181, 139})) //输出:测
fmt.Println(string([]uint8{230, 181, 139, 230, 181, 139})) //输出:测测
// 整数
fmt.Println(uint8(97)) //输出:97
fmt.Println(string(97)) //输出:a
// 切片
u = "a"
fmt.Println(u[0:1]) //输出:a
fmt.Println(string([]uint8{97})) //输出:a
fmt.Println(string([]uint8{97, 97})) //输出:aa
// 27979
// 测
// 测
// 测
// [27979]
// 测
// 测测
// 测
// 测测
// 97
// a
// a
// a
// aa
参考文章:
《Golang学习日志 ━━ 一图一代码看懂range、byte、rune、uint8、int32》
《Go语言圣经-字节切片与字符串》