题意:给出n个字符串,求其公共子串
思路:两两求子串,LCP(S1,S2,....) = LCP(S1, LCP(S2,....))
代码 如下:
func longestCommonPrefix(strs []string) string {
if strs == nil || len(strs) == 0 {
return ""
}
var prefix = strs[0]
for i := 1; i < len(strs); i++ {
for {
if strings.Index(strs[i], prefix) != 0 {
prefix = prefix[0 : len(prefix)-1]
if len(prefix) == 0 {
return ""
}
} else {
break
}
}
}
return prefix
}
解法二:
按列扫描,以第一个字符串为基准,依次判断其余字符串对应的列是否与第一个字符串对应列相等,如果不相等,就取上一次比较的列位置
代码 如下:
func longestCommonPrefix(strs []string) string {
if strs == nil || len(strs) == 0 {
return ""
}
for i := 0; i < len(strs[0]); i++ {
ch := strs[0][i]
for j := 1; j < len(strs); j++ {
if i == len(strs[j]) || strs[j][i] != ch {
return strs[0][0:i]
}
}
}
return strs[0]
}
解法三:后缀数组方法
将字符串数组合并成一个字符串,计算其sa数组,rank数组,height数组,使用sparse-table计算height的最长公共子串长度
注意,因为计算的最长公共子串长度可能超过字符串数组中字符串本身长度,需要将height的最长公共子串长度与字符串数组中的字符串长度作比较,求其中的最小值
代码如下:
const MAXN = 10100
func buildSa(str string) []int {
strLen := len(str)
rank := make([]int, strLen)
y := make([]int, strLen)
sa := make([]int, strLen)
c := make([]int, MAXN)
m := len(c)
for i := 0; i < strLen; i++ {
rank[i] = int(str[i])
c[rank[i]]++
}
for i := 1; i < m; i++ {
c[i] += c[i - 1]
}
for i := strLen - 1; i >= 0; i-- {
c[rank[i]]--
sa[c[rank[i]]] = i
}
for j := 1; j <= strLen; j <<= 1 {
//第一关键字排序
p := 0
for i := strLen - j; i < strLen; i++ {
y[p] = i
p++
}
for i := 0; i < strLen; i++ {
if sa[i] >= j {
y[p] = sa[i] - j
p++
}
}
//第二关键字排序
for i := 0; i < m; i++ {
c[i] = 0
}
for i := 0; i < strLen; i++ {
c[rank[y[i]]]++
}
for i := 1; i < m; i++ {
c[i] += c[i - 1]
}
for i := strLen - 1; i >= 0; i-- {
c[rank[y[i]]]--
sa[c[rank[y[i]]]] = y[i]
}
rank, y = y, rank
rank[sa[0]] = 0
p = 0
for i := 1; i < strLen; i++ {
if (y[sa[i - 1]] == y[sa[i]]) && (sa[i - 1] + j < strLen) && (sa[i] + j < strLen) && (y[sa[i - 1] + j] == y[sa[i] + j]) {
rank[sa[i]] = p
} else {
p++
rank[sa[i]] = p
}
}
if p >= strLen {
break
}
m = p + 1
}
return sa
}
func getHeight(sa []int, str string) []int {
rank := make([]int, len(sa))
height := make([]int, len(sa))
for k, v := range sa {
rank[v] = k
}
Len := len(sa)
k := 0
for i := 0; i < Len; i++ {
if k > 0 {
k--
}
pos := rank[i]
if pos == 0 {
continue
}
pos--
pos = sa[pos]
for i + k < Len && pos + k < Len && str[i + k] == str[pos + k] {
k++
}
height[rank[i]] = k
}
return height
}
func RMQInit(h []int) [][]int {
Len := len(h)
var ans [][]int
for i := 0; i < Len; i++ {
var tmp []int
tmp = append(tmp, h[i])
ans = append(ans, tmp)
}
for j := 1; (1 << uint(j)) <= Len; j++ {
for i := 0; i + (1 << uint(j)) - 1 < Len; i++ {
var tmp int
if ans[i][j - 1] < ans[i + (1 << uint(j - 1))][j - 1] {
tmp = ans[i][j - 1]
} else {
tmp = ans[i + (1 << uint(j - 1))][j - 1]
}
ans[i] = append(ans[i], tmp)
}
}
return ans
}
func RMQQuery(ans [][]int, l, r int) int {
k := 0
Len := r - l + 1
for (1 << uint(k + 1)) <= Len {
k++
}
if ans[l][k] < ans[r - (1 << uint(k)) + 1][k] {
return ans[l][k]
} else {
return ans[r - (1 << uint(k)) + 1][k]
}
}
func longestCommonPrefix(strs []string) string {
if nil == strs || 0 == len(strs) {
return ""
}
if 1 == len(strs) {
return strs[0]
}
var combStr string
for i:= 0; i < len(strs); i++ {
if 0 >= len(strs[i]) {
return ""
}
combStr += strs[i]
}
sa := buildSa(combStr)
if 0 >= len(sa) {
return ""
}
rank := make([]int, len(sa))
for k, v := range sa {
rank[v] = k
}
height := getHeight(sa, combStr)
rmq := RMQInit(height)
var (
min int
max int
pos int
)
pos = 0
min = len(combStr)
for i := 0; i < len(strs); i++ {
if rank[pos] > max {
max = rank[pos]
}
if rank[pos] < min {
min = rank[pos]
}
pos += len(strs[i])
}
ans := RMQQuery(rmq, min + 1, max)
for i := 0; i < len(strs); i++ {
if ans > len(strs[i]) {
ans = len(strs[i])
}
}
return strs[0][0:ans]
}