抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

1.1 字符串移位

问题:将字符的前k个字符移到字符串结尾。
Input:“abcde”,2
Output:“cdeab”

三步翻转法: 将字符串分为前k位和后(n-k)位两部分,将两部分分别翻转,最后再整体翻转即可。
时间复杂度:T = O(n)
参考原文

示例代码:

// 反转字符串
func reverse(s string) string {
    runes := []rune(s)
    for l, r := 0, len(runes) - 1; l < r; l, r = l + 1, r - 1 {
        runes[l], runes[r] = runes[r], runes[l]
    }
    return string(runes)
}

// 三步反转法对字符串进行循环移位
func shift_string(s string, k int) string {
    runes := []rune(s)
    ls, rs := runes[:k], runes[k:]
    rls := reverse(string(ls))
    rrs := reverse(string(rs))
    return reverse(rls + rrs)
}

1.2 最长回文子串

LeetCode No.5

问题:找出一个子串包含的最长回文子串。
Input: s = “babad”
Output: “bab”
Note: “aba” is also a valid answer.

解法1:从某个子串向两边扩展,遍历找出最长的一个(需要考虑偶数个、奇数个字符的回文子串)

示例代码

func LongestPalindrome(s string) string {
    rs := []rune(s)
    mx, st := 0, 0
    i, j, c := 0, 0, 0
    for i = 0; i < len(rs); i++ {
        // 奇数个字符的回文子串
        for j = 0; i - j >= 0 && i + j < len(rs); j++ {
            if rs[i - j] != rs[i + j] {
                break
            }
            c = 2 * j + 1
        }
        if c > mx {
            mx = c
            st = i - j + 1
        }
        // 偶数个字符的回文子串
        for j = 0; (i - j) >= 0 && (i + j + 1) < len(rs); j++ {
            if rs[i - j] != rs[i + j + 1] {
                break
            }
            c = j * 2 + 2
        }
        if c > mx {
            mx = c
            st = i - j + 1
        }
    }

    return string(rs[st: st + mx])
}

解法2:马拉车算法
具体思路参考原文

示例代码:

func min(x, y int) int {
    if x < y {
        return x
    }
    return y
}
func LongestPalindrome1(s string) string {
    if len(s) < 2 {
        return s
    }
    news := make([]rune, len(s))
    news[0] = '#'
    for _, r := range s {
        news = append(news, r)
        news = append(news, '#')
    }
    dp := make([]int, len(news))
    mx, center, maxlen, maxst := 0, 0, 1, 0
    for i := 0; i < len(news); i++ {
        // 算法核心转移方程
        if i < mx {
            dp[i] = min(mx - i, dp[2*center - i])
        }

        // 以i为中心,只接从距离i为d[i] + 1的位置扩散
        left, right := i - (1 + dp[i]), i + (1+ dp[i])
        for left >= 0 && right < len(news) && news[left] == news[right] {
            dp[i]++
            left++
            right--
        }
        // 更新mx
        if i + dp[i] > mx {
            mx = i + dp[i]
            center = i
        }
        // 更新最大长度和对应在源字符串的起始位置
        if dp[i] > maxlen {
            maxlen = dp[i]
            maxst = (i - maxlen) / 2
        }
    }
    return s[maxst : maxst + maxlen]
}

1.3 字符串的全排列

问题描述:输入一个字符串,打印出该字符串中字符的所有排列。
例如输入字符串abc,则输出由字符a、b、c 所能排列出来的所有字符串
abc、acb、bac、bca、cab 和 cba。

解法1:递归方法DFS搜索,想象成树

示例代码:

var res = []string{}

func dfs_search(s string, lv int, cur string)  {
    if lv == 0 {
        res = append(res, cur)
    }
    for i := 0; i < len(s); i++ {
        // 去掉当前字符,下一层在剩下的字符中挑
        dfs_search(s[:i] + s[i+1:], lv - 1, cur + string(s[i]))
    }
}

func FullPermutation(s string) []string  {
    res = []string{}
    dfs_search(s, len(s), "")
    return res
}

解法2:字典序排列,从当前字符串s生成刚好比他大的下一个字符串排列
参考原文

  1. 找到排列中最后一个升序的位置i
  2. 找到i后面最后一个比s[i]大的位置j
  3. 交换s[i]和s[j]
  4. 将i+1之后的字符串反转

示例代码:

// 字典序排列算法
func next_permutation(s string) (bool, string)  {
    rs := []rune(s)
    i, j := 0, 0

    // 找到最后一个升序的位置i
    for i = len(rs) - 2; i >= 0 && rs[i] >= rs[i+1]; i-- {}
    if i < 0 {
        return false, ""
    }
    // 找到i后面最后一个比rs[i]大的位置j
    for j = len(rs) - 1; j > i && rs[j] <= rs[i]; j-- {}
    // 交换rs[i], s[j]
    rs[i], rs[j] = rs[j], rs[i]
    // 反转rs[i+1:]
    rev := reverse(string(rs[i + 1:]))
    return true, string(rs[:i+1]) + rev
}

func DictOrderFullPermutation(s string) []string  {
    res := []string{}
    for ok, next_str := true, s; ok; ok, next_str = next_permutation(next_str) {
        res = append(res, next_str)
    }
    return res
}

评论