biweekly contest 140。
我搞了半天的 rolling hash 竟然被卡常數,真搞不懂時間限制的標準。

題目

輸入兩個字串 s 和 pattern。

若一個字串 x 在修改至多一個字元後等同於字串 y,則稱 x 和 y 幾乎相等

回傳最小的 s 的子字串起始索引,其子字串與 pattern 幾乎相等
若不存在則回傳 -1。

解法

修改 pattern 至多一個字元,可能的結果有 pattern 本身,或是任意 pattern[i] 修改成任意字母。
總共 26 * N = 2e6 種可能。

透過 rolling hash 將所有可能的子字串加入集合中,最後再枚舉 s 中的子字串,第一個找到的就是答案。
可惜會超時,只能想想別的辦法。


Q3 的時候是求子序列,我們利用了前後綴分解的技巧找到分割點,使得左右兩邊的匹配長度至少等於 N-1。
雖然本題 Q4 是求子字串,但同樣也適用前後綴分解的思維。

設一個子字串 sub = s[i..j] 幾乎相等於 pattern。
那麼兩者肯定具有公用前綴 pref = sub[i..] 還有公共後綴 suff = sub[..j],並有 len(pref) + len(suff) >= N-1。


問題轉換成:找 sub 和 pattern 的公共前綴公共後綴
以前周賽中也碰過不少次,正是 z-function。

但是原始的 z-function 是在字串 s 本身找子字串 s[i..] 的最長公共前綴
此處是要在 s 裡找 pattern,所以需要將兩者串接為 pattern + “#” +s。
其中 “#” 號是只是分隔習慣,不加也可以。

至於後綴,只需把 s 和 pattern 都反轉後,另外套一次 z-function 即可。
注意:z[i] 指的是 text = pattern + “#” + s,取 z[i] 值記得加上 len(pattern) + 1 的偏移量。
注意:z[i] 是從左向右數,所以後綴 z-array 的索引也要反轉,然後再加上偏移量。

時間複雜度 O(N)。
空間複雜度 O(N)。

class Solution:
    def minStartingIndex(self, s: str, pattern: str) -> int:
        M, N = len(s), len(pattern)
        pre_z = z_function(pattern + "#" + s)
        suf_z = z_function(pattern[::-1] + "#" + s[::-1])

        # check substring s[i..j]
        # where pre[i] + suf[j] >= N-1
        for i in range(M - N + 1):
            j = i + N - 1
            rev_j = M - 1 - j  # reverse j
            pre_i = i + N + 1  # offset len(pattern + "#")
            suf_j = rev_j + N + 1  # offset len(pattern + "#")
            if pre_z[pre_i] + suf_z[suf_j] >= N - 1:
                return i

        return -1


def z_function(s):
    N = len(s)
    z = [0]*N
    L = R = 0
    for i in range(1, N):
        if R < i:  # not covered by previous z-box
            # z[i] = 0
            pass
        else:  # partially or fully covered
            j = i-L
            if j+z[j] < z[L]:  # fully covered
                z[i] = z[j]
            else:
                z[i] = R-i+1

        while i+z[i] < N and s[i+z[i]] == s[z[i]]:  # remaining substring
            z[i] += 1
        if i+z[i]-1 > R:  # R out of prev z-box, update R
            L = i
            R = i+z[i]-1
    return z