【2022-10-31每日一题】481. 神奇字符串[Medium]

2022-10-31
2分钟阅读时长

2022-10-31每日一题:481. 神奇字符串

难度:Medium

标签:双指针 、 字符串

神奇字符串 s 仅由 '1''2' 组成,并需要遵守下面的规则:

  • 神奇字符串 s 的神奇之处在于,串联字符串中 '1''2' 的连续出现次数可以生成该字符串。

s 的前几个元素是 s = "1221121221221121122……" 。如果将 s 中连续的若干 12 进行分组,可以得到 "1 22 11 2 1 22 1 22 11 2 11 22 ......" 。每组中 1 或者 2 的出现次数分别是 "1 2 2 1 1 2 1 2 2 1 2 2 ......" 。上面的出现次数正是 s 自身。

给你一个整数 n ,返回在神奇字符串 s 的前 n 个数字中 1 的数目。

 

示例 1:

输入:n = 6
输出:3
解释:神奇字符串 s 的前 6 个元素是 “122112”,它包含三个 1,因此返回 3 。 

示例 2:

输入:n = 1
输出:1

 

提示:

  • 1 <= n <= 105

方法一:双指针

详细思路过程见官方题解,这里只做个人刷题记录,方便后续查询阅读

// 速度最快
func magicalString(n int) int {
    // 122 n小于4直接返回1
    if n < 4 {
        return 1
    }
    s := make([]byte, n)
    copy(s, "122") // 初始化前三个位置
    ans := 1
    for i, j := 2, 3; j < n; i++ {
        num := 3 - (s[j-1] - '0')
        for size := s[i] - '0'; size > 0 && j < n; j++ {
            s[j] = num + '0'
            if num == 1 {
                ans++
            }
            size--
        }
    }
    return ans
}

// 写法二
func magicalString(n int) (ans int) {
    s := make([]int, 0, n)
    s = append(s, 1, 2, 2)
    for i := 2; len(s) < n; i++ {
        pre := s[len(s)-1]
        cur := 3 - pre
        for j := 0; j < s[i]; j++ {
            s = append(s, cur)
        }
    }
    for _, c := range s[:n] {
        if c == 1 {
            ans++
        }
    }
    return ans
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)。

方法二:构造

写法1

func magicalString(n int) int {
    s := make([]byte, 0, n + 1)
    s = append(s, 1, 2, 2)
    c := []byte{2}
    for i := 2; len(s) < n; i++ {
        // 1^3=2, 2^3=1,在 1 和 2 之间转换
        c[0] ^= 3
        s = append(s, bytes.Repeat(c, int(s[i]))...)
    }
    return bytes.Count(s[:n], []byte{1})
}

打表法

const mx int = 1e5
var acc [mx + 1]int

func init() {
    s := make([]byte, 0, mx+1)
    s = append(s, 1, 2, 2)
    c := []byte{2}
    for i := 2; len(s) < mx; i++ {
        // 1^3=2, 2^3=1,这样能在 1 和 2 之间转换
        c[0] ^= 3 
        s = append(s, bytes.Repeat(c, int(s[i]))...)
    }
    for i := 0; i < mx; i++ {
        acc[i+1] = acc[i] + int(2-s[i]) // 2-1=1,2-2=0,这样就只统计了 1
    }
}

func magicalString(n int) int {
    return acc[n]
}

LeetCode题库地址