(字符串扫描)最长公共前缀


题目内容

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 1:

输入: ["flower","flow","flight"]
输出: "fl"

示例 2:

输入: ["dog","racecar","car"]
输出: ""

解释: 输入不存在公共前缀。
说明:

所有输入只包含小写字母 a-z 。

方法一: 横向扫描

依次遍历字符串数组中的每个字符串,对于每个遍历到的字符串,更新最长公共前缀,当遍历完所有的字符串以后,即可得到字符串数组中的最长公共前缀。

说人话就是我们直接一个一个的比,先获取第一个和第二个的相同前缀,然后我们拿这个相同的前缀和第三个比最后依次比完,得到最长的公共前缀

示例代码:

package main

import "log"

func longestCommonPrefix(strs []string) string {
	if len(strs) == 0 {
		return ""
	}
	prefix := strs[0]
	count := len(strs)
	for i := 1; i < count; i++ {
		prefix = lcp(prefix, strs[i])
		if len(prefix) == 0 {
			break
		}
	}
	return prefix
}

func lcp(str1, str2 string) string {
	length := min(len(str1), len(str2))
	index := 0
	for index < length && str1[index] == str2[index] {
		index++
	}
	return str1[:index]
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

func main() {
	text := longestCommonPrefix([]string{"ab", "abc", "ad"})
	log.Println(text)
}

方法二:纵向扫描

纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。

纵向扫描更简单,就是直接遍历所有的字符串,然后从第一位开始比较,如果发现不同的那么就直接退出

参考代码:

package main

import "log"

func longestCommonPrefix(strs []string) string {
	if len(strs) == 0 {
		return ""
	}
	for i := 0; i < len(strs[0]); i++ {
		for j := 1; j < len(strs); j++ {
			if i == len(strs[j]) || strs[j][i] != strs[0][i] {
				return strs[0][:i]
			}
		}
	}
	return strs[0]
}

func main() {
	text := longestCommonPrefix([]string{"ab", "abc", "ad"})
	log.Println(text)
}

方法三: 分治法

这个方法看起来不明所以,实际上就是利用了递归的思想,比对这么多很麻烦,我们就把这这个数组分成两部分,这里两部分继续细分,直到只剩下两个字符串为止,这样我们就可以直接比对这两个字符串,返回结果,然后再递归回去。。

首先我们看代码,先找中值,然后这里再调用自己本身获取左边的值和右边的值,我们这里先不管递归,这里执行完毕后会只返回左边的字符串和右边的字符串,最后我们只需要找到这两个字符串的公共前缀就可以了。

参考代码

package main

import "log"

func longestCommonPrefix(strs []string) string {
	if len(strs) == 0 {
		return ""
	}
	var lcp func(int, int) string
	lcp = func(start, end int) string {
		if start == end {
			return strs[start]
		}
		mid := (start + end) / 2
		lcpLeft, lcpRight := lcp(start, mid), lcp(mid+1, end)
		minLength := min(len(lcpLeft), len(lcpRight))
		for i := 0; i < minLength; i++ {
			if lcpLeft[i] != lcpRight[i] {
				return lcpLeft[:i]
			}
		}
		return lcpLeft[:minLength]
	}
	return lcp(0, len(strs)-1)
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

func main() {
	text := longestCommonPrefix([]string{"ab", "abc", "abdd"})
	log.Println(text)
}

方法四: 二分查找

首先最重要的就是找到最短的那个字符串,找到之后我们就可以开始比较了,通过一个low和height来比较,low为0,height为最短字符串长度,我们先取中间值,然后比对所有字符串,判断这个前缀是否相等,如果相等那么low就等于中间值,然后再取low和height的中间值,如果不相等,那么就说明前缀要比中间值小,我们的height就直接设置为中值减一,直到low等于height就说明找到最短前缀了

package main

import "log"

func longestCommonPrefix(strs []string) string {
	// 如果数组长度为0,那么直接返回空
	if len(strs) == 0 {
		return ""
	}
	// 这个函数用于判断指定长度下所有的字符数组的前缀是否相等
	isCommonPrefix := func(length int) bool {
		str0, count := strs[0][:length], len(strs)
		for i := 1; i < count; i++ {
			if strs[i][:length] != str0 {
				return false
			}
		}
		return true
	}
	// 首先我们假设距离最短的就是列表的第一个值
	minLength := len(strs[0])
	// 这里我们通过遍历,获取到最短字符串的长度
	for _, s := range strs {
		if len(s) < minLength {
			minLength = len(s)
		}
	}
	low, high := 0, minLength
	// 这里我们通过low和height来判断最长的公共前缀,当low等于height的时候,就说明已经找到了
	for low < high {
		// 这里我们先取中值
		mid := (high-low+1)/2 + low
		// 判断中值的前缀是否相等,相等的话就说明最长的公共前缀肯定大于mid,我们可以把low增加
		if isCommonPrefix(mid) {
			low = mid
		} else {
			// 这里就说明中值的前缀不相等,我们这里的height就可以比low小一个,然后我们再继续比较
			high = mid - 1
		}
	}
	return strs[0][:low]
}

func main() {
	text := longestCommonPrefix([]string{"ab", "abc", "abdd"})
	log.Println(text)
}

文章作者: 小游
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小游 !
  目录