LeetCode 题解工作台

字符串转换

给你两个长度都为 n 的字符串 s 和 t 。你可以对字符串 s 执行以下操作: 将 s 长度为 l ( 0 )的 后缀字符串 删除,并将它添加在 s 的开头。 比方说, s = 'abcd' ,那么一次操作中,你可以删除后缀 'cd' ,并将它添加到 s 的开头,得到 s = 'cdab' 。 给…

category

4

题型

code_blocks

3

代码语言

hub

3

相关题

当前训练重点

困难 · 状态·转移·动态规划

bolt

答案摘要

""" DP, Z-algorithm, Fast mod.

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

给你两个长度都为 n 的字符串 s 和 t 。你可以对字符串 s 执行以下操作:

  • s 长度为 l (0 < l < n)的 后缀字符串 删除,并将它添加在 s 的开头。
    比方说,s = 'abcd' ,那么一次操作中,你可以删除后缀 'cd' ,并将它添加到 s 的开头,得到 s = 'cdab' 。

给你一个整数 k ,请你返回 恰好 k 次操作将 s 变为 t 的方案数。

由于答案可能很大,返回答案对 109 + 7 取余 后的结果。

 

示例 1:

输入:s = "abcd", t = "cdab", k = 2
输出:2
解释:
第一种方案:
第一次操作,选择 index = 3 开始的后缀,得到 s = "dabc" 。
第二次操作,选择 index = 3 开始的后缀,得到 s = "cdab" 。

第二种方案:
第一次操作,选择 index = 1 开始的后缀,得到 s = "bcda" 。
第二次操作,选择 index = 1 开始的后缀,得到 s = "cdab" 。

示例 2:

输入:s = "ababab", t = "ababab", k = 1
输出:2
解释:
第一种方案:
选择 index = 2 开始的后缀,得到 s = "ababab" 。

第二种方案:
选择 index = 4 开始的后缀,得到 s = "ababab" 。

 

提示:

  • 2 <= s.length <= 5 * 105
  • 1 <= k <= 1015
  • s.length == t.length
  • s 和 t 都只包含小写英文字母。
lightbulb

解题思路

方法一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""
DP, Z-algorithm, Fast mod.
Approach
How to represent a string?
Each operation is just a rotation. Each result string can be represented by an integer from 0 to n - 1. Namely, it's just the new index of s[0].
How to find the integer(s) that can represent string t?
Create a new string s + t + t (length = 3 * n).
Use Z-algorithm (or KMP), for each n <= index < 2 * n, calculate the maximum prefix length that each substring starts from index can match, if the length >= n, then (index - n) is a valid integer representation.
How to get the result?
It's a very obvious DP.
If we use an integer to represent a string, we only need to consider the transition from zero to non-zero and from non-zero to zero. In other words, all the non-zero strings should have the same result.
So let dp[t][i = 0/1] be the number of ways to get the zero/nonzero string
after excatly t steps.
Then
dp[t][0] = dp[t - 1][1] * (n - 1).
All the non zero strings can make it.
dp[t][1] = dp[t - 1][0] + dp[t - 1] * (n - 2).
For a particular non zero string, all the other non zero strings and zero string can make it.
We have dp[0][0] = 1 and dp[0][1] = 0
Use matrix multiplication.
How to calculate dp[k][x = 0, 1] faster?
Use matrix multiplication
vector (dp[t - 1][0], dp[t - 1][1])
multiplies matrix
[0 1]
[n - 1 n - 2]
== vector (dp[t][0], dp[t - 1][1]).
So we just need to calculate the kth power of the matrix which can be done by fast power algorith.
Complexity
Time complexity:
O(n + logk)
Space complexity:
O(n)
"""


class Solution:
    M: int = 1000000007

    def add(self, x: int, y: int) -> int:
        x += y
        if x >= self.M:
            x -= self.M
        return x

    def mul(self, x: int, y: int) -> int:
        return int(x * y % self.M)

    def getZ(self, s: str) -> List[int]:
        n = len(s)
        z = [0] * n
        left = right = 0
        for i in range(1, n):
            if i <= right and z[i - left] <= right - i:
                z[i] = z[i - left]
            else:
                z_i = max(0, right - i + 1)
                while i + z_i < n and s[i + z_i] == s[z_i]:
                    z_i += 1
                z[i] = z_i
            if i + z[i] - 1 > right:
                left = i
                right = i + z[i] - 1
        return z

    def matrixMultiply(self, a: List[List[int]], b: List[List[int]]) -> List[List[int]]:
        m = len(a)
        n = len(a[0])
        p = len(b[0])
        r = [[0] * p for _ in range(m)]
        for i in range(m):
            for j in range(p):
                for k in range(n):
                    r[i][j] = self.add(r[i][j], self.mul(a[i][k], b[k][j]))
        return r

    def matrixPower(self, a: List[List[int]], y: int) -> List[List[int]]:
        n = len(a)
        r = [[0] * n for _ in range(n)]
        for i in range(n):
            r[i][i] = 1
        x = [a[i][:] for i in range(n)]
        while y > 0:
            if y & 1:
                r = self.matrixMultiply(r, x)
            x = self.matrixMultiply(x, x)
            y >>= 1
        return r

    def numberOfWays(self, s: str, t: str, k: int) -> int:
        n = len(s)
        dp = self.matrixPower([[0, 1], [n - 1, n - 2]], k)[0]
        s += t + t
        z = self.getZ(s)
        m = n + n
        result = 0
        for i in range(n, m):
            if z[i] >= n:
                result = self.add(result, dp[0] if i - n == 0 else dp[1])
        return result
speed

复杂度分析

指标
时间Depends on the final approach
空间Depends on the final approach
psychology

面试官常问的追问

外企场景
  • question_mark

    Candidate demonstrates understanding of state transition dynamic programming.

  • question_mark

    Candidate recognizes the importance of string rotations and modulo arithmetic in handling large numbers.

  • question_mark

    Candidate should explain how to optimize for large inputs and the trade-offs between time and space complexity.

warning

常见陷阱

外企场景
  • error

    Forgetting to handle the modulo operation, leading to overflow or incorrect answers.

  • error

    Misunderstanding the rotation property of string t and assuming it’s not derived from s.

  • error

    Overcomplicating the DP state transitions, missing out on optimizing the table for efficient solutions.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    What if k is much smaller than the number of operations required? Consider edge cases where k is limited.

  • arrow_right_alt

    How would you approach this problem if we allowed substring rotations instead of suffix rotations?

  • arrow_right_alt

    What if we need to count the transformations modulo some different number instead of 10^9 + 7?

help

常见问题

外企场景

字符串转换题解:状态·转移·动态规划 | LeetCode #2851 困难