You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

咨询:如何利用KMP算法以O(n)复杂度计算使01串成为回文串的最少开头删除次数

用KMP算法解决「从开头删最少字符得回文串」问题

这问题我之前也琢磨过,用KMP来优化确实能把时间复杂度压到O(n),思路其实挺巧妙的,我给你一步步拆解清楚:

问题转化

首先,咱们得把问题换个角度看:要找原字符串中最长的、以字符串结尾的回文子串。因为删掉开头的字符后,剩下的是原串的后缀,只要这个后缀是回文,那删除的字符数就是总长度减去这个最长回文后缀的长度——这肯定是最少的删除量,毕竟保留的回文越长,删的就越少。

KMP前缀函数的作用

KMP里的前缀函数(也就是常说的next数组),核心是对字符串的每个位置,计算「前i+1个字符中,最长的相等前缀和后缀的长度」。这个特性刚好能帮我们找到原串后缀和反转串前缀的最长匹配——而这个匹配的长度,就是我们要找的最长回文后缀的长度。

具体实现步骤

  1. 构造拼接字符串:把原字符串S、一个分隔符(比如#,因为原串只有0和1,不会冲突)、反转后的原字符串S_rev拼在一起,得到combined = S + '#' + S_rev。加分隔符是为了避免匹配时跨原串和反转串的情况,确保我们只匹配原串的后缀和反转串的前缀。
  2. 计算前缀函数:对combined字符串计算前缀函数数组pi
  3. 得到结果pi数组的最后一个元素,就是原串中最长回文后缀的长度。用原串总长度减去这个值,就是需要从开头删除的最少字符数。

代码示例(Python)

def compute_prefix_function(s):
    n = len(s)
    pi = [0] * n
    for i in range(1, n):
        j = pi[i-1]
        # 回溯找到最长的匹配前缀
        while j > 0 and s[i] != s[j]:
            j = pi[j-1]
        if s[i] == s[j]:
            j += 1
        pi[i] = j
    return pi

def min_deletions_to_palindrome(s):
    reversed_s = s[::-1]
    # 构造带分隔符的拼接字符串,避免跨串匹配
    combined = s + '#' + reversed_s
    pi = compute_prefix_function(combined)
    # 最长回文后缀的长度就是pi数组最后一个值
    max_palindrome_len = pi[-1]
    return len(s) - max_palindrome_len

# 测试用例
print(min_deletions_to_palindrome("01101"))  # 输出2:删除前2个字符,剩余"101"是回文
print(min_deletions_to_palindrome("01001"))  # 输出1:删除第一个"0",剩余"1001"是回文
print(min_deletions_to_palindrome("0101"))   # 输出1:删除第一个"0",剩余"101"是回文

为什么这个方法有效?

因为回文串的定义是「正读和反读一样」,所以原串的某个后缀是回文,等价于这个后缀等于它自己的反转,也就是等于原串反转后的对应前缀。而我们构造的combined字符串,通过前缀函数找到的最长公共前后缀,刚好就是原串后缀和反转串前缀的最长匹配——这个匹配的长度,就是最长回文后缀的长度。

整个过程的时间复杂度是O(n),因为前缀函数的计算是线性的,拼接后的字符串长度是2n+1,完全符合你要的优于O(n²)的要求。

内容的提问来源于stack exchange,提问作者Ignos

火山引擎 最新活动