📌  相关文章
📜  最小化要更改的字符,以使字符串的左右旋转相同(1)

📅  最后修改于: 2023-12-03 14:55:21.485000             🧑  作者: Mango

最小化要更改的字符,以使字符串的左右旋转相同

在这个问题中,我们需要找到最小的要更改的字符数量,以使一个字符串在左右旋转之后变得相同。例如,字符串 "abcd" 左旋转一位得到 "bcda",右旋转一位得到 "dabc",它们并不相同。但是,如果将 "abcd" 左旋转三位,则得到 "dabc",这个字符串右旋转一位后也是 "dabc",它们相同。

解决方案
方法一:暴力枚举

首先,我们可以用暴力枚举的方法,尝试所有可能的旋转方式,然后逐个比较它们是否相同。具体地,对于一个长度为 n 的字符串,我们可以依次尝试将它向左旋转 1, 2, ..., n-1 位,共尝试 n-1 种方案。对于每种方案,我们需要将得到的字符串向右旋转 0, 1, ..., n-1 位,共尝试 n 种方案。因此,我们需要执行的比较次数为 O(n^3)。由于这个算法的时间复杂度太高,因此它不适用于较长的字符串。

def get_min_changes(s: str) -> int:
    n = len(s)
    ans = n
    for i in range(1, n):
        for j in range(n):
            if s[(j+i)%n] != s[(j+i-1)%n]:
                break
        else:
            ans = min(ans, i)
    return ans
方法二:KMP 算法

上面的暴力枚举算法并不可取,我们需要一个更快的算法。另一种方法是使用 KMP 算法来寻找字符串的循环节。具体地,我们首先计算出字符串 s 的 next 数组,然后从 next[n-1] 开始向前查找 next 数组中的循环节。如果 next[i] 等于 next[j],并且 j-i 是一个循环节的长度,则我们不需要更改字符串 s 的前 i+1 个字符,就可以让字符串 s 在旋转 i+1 个位置后变得相同。我们可以依次尝试所有可能的 i 值,最终在所有的方案中选取最小的修改次数。

def get_min_changes(s: str) -> int:
    n = len(s)
    nxt = [-1] * n
    j = -1
    for i in range(1, n):
        while j >= 0 and s[j+1] != s[i]:
            j = nxt[j]
        if s[j+1] == s[i]:
            j += 1
        nxt[i] = j
    
    ans = n
    for i in range(n-1, nxt[n-1], -1):
        if (i+1) % (i-nxt[i]) == 0:
            ans = min(ans, i-nxt[i])
    return ans
方法三:Manacher 算法

另一种常见的字符串匹配算法是 Manacher 算法。该算法可以用于查找具有某种特殊性质的回文子串。在本问题中,我们需要寻找长度为偶数的回文子串。因此,我们可以将字符串 s 复制一遍,成为 s+s,然后用 Manacher 算法寻找它的最长回文子串。这个回文子串就是字符串 s 的最长循环节。依据这个循环节,我们可以计算出字符串 s 在不同位置旋转之后的值,然后逐个比较这些值,找到最小的修改次数。

def get_min_changes(s: str) -> int:
    n = len(s)
    t = "#" + "#".join(s) + "#"
    f = [0] * len(t)
    c, r = 0, 0
    for i in range(1, len(t)):
        if r > i:
            f[i] = min(r-i, f[2*c-i])
        while i+f[i] < len(t) and i-f[i] >= 0 and t[i+f[i]] == t[i-f[i]]:
            f[i] += 1
        if i+f[i] > r:
            c, r = i, i+f[i]
    
    ans = n
    for i in range(1, n):
        if f[2*i+1] >= i+1:
            ans = min(ans, n-2*i-1)
    return ans
总结

在本问题中,我们需要找到最小的要更改的字符数量,以使一个字符串在左右旋转之后变得相同。我们可以使用暴力枚举、KMP 算法、Manacher 算法等不同的算法来解决这个问题。其中,KMP 算法和 Manacher 算法具有线性时间复杂度,因此它们更适用于较长的字符串。