【力扣每日一题】回文子串

题目描述

原题链接

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

输入:“abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”

示例 2:

输入:“aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”

提示:

输入的字符串长度不会超过 1000 。

解题思路:运用Manacher算法*

  • 为了消除奇数偶数带来的不必要的麻烦,在字符串中插入‘#’,例如,aaa插入之后就变成了#a#a#a#,保证了每一个字符串都是奇数长度的,假设原字符串是S,那么处理过后的字符串就是s。

  • 我们用 f(i)来表示以 s 的第 i 位为回文中心,可以拓展出的最大回文半径,那么 f(i) - 1 就是以 i 为中心的最大回文串长度 (假设原字符串中,第i个字符最大回文串长度为L,经过马拉车算法处理后的回文字符串长度增加了L+1,总长度为2L+1.已知在处理后的字符串中,第i个字符的最大回文半径为f(i), 则其回文字符串长度为2*f(i)-1 = 2L+1, 由此可知 L = f(i)-1)

  • 枚举每一个位置,以它作为回文中心,但是于中心拓展不同的是,它会利用已经计算好的状态来进行更新。具体地说,假设我们已经计算好了 [1, i - 1] 区间内所有点的 f(即我们知道 [1, i - 1] 这些点作为回文中心时候的最大半径), 那么我们也就知道了 [1, i - 1] 拓展出的回文达到最大半径时的回文右端点。例如 i = 4的时候 f(i) = 5,说明以第 4 个元素为回文中心,最大能拓展到的回文半径是 5,此时右端点为 4 + 5 - 1 = 8。所以当我们知道一个 i 对应的 f(i)的时候,我们就可以很容易得到它的右端点为 i + f(i) - 1。

  • Manacher 算法如何通过已经计算出的状态来更新 f(i) 呢?Manacher 算法要求我们维护「当前最大的回文的右端点 r_m」以及这个回文右端点对应的回文中心 i_m 。我们需要顺序遍历 s,假设当前遍历的下标为 i。我们知道在求解 f(i) 之前我们应当已经得到了从 [1, i - 1]所有的 f,并且当前已经有了一个最大回文右端点 r_m以及它对应的回文中心 i_m 。

初始化

  • 如果 i≤r m,说明 i被包含在当前最大回文子串内,假设 j 是 i关于这个最大回文的回文中心 i_m的对称位置(即j+i=2×i m ),我们可以得到 f(i)至少等于 min{f(j),r m−i+1}。这里将 f(j) 和 r_m - i + 1 取小,是先要保证这个回文串在当前最大回文串内。下面是解释。

  • —X----j---------Im---------i------Rm—

  • 上图中,以im为中心的最长回文字符串的右端端点为Rm, 如果i<=Rm,说明 i 被包含在im为中心的最长回文字符串中,假设j是最长回文字符串中i在左侧的对称位置,再假设X是最长回文串Rm对应的最左端断点,其中X<=j,已知以j为中心的回文字符串长度为f(j), f(i)的初始化分下列两种情况:

  • case 1: 如下图所示, 如果j的最长回文串的最左侧端点y不在X的左侧(在右侧或者与X重合),说明y也包含在i的最长回文字符串中,根据i和j的对应关系可知f(i) >= f(j),并且f(j) <= Rm -i+1, 因此把f(i)初始化为f(j)。

  • —X–y--j–y------Im-------y–i--y----Rm—

  • case 2: 如下图所示,如果j为中心的最长回文串的最左侧端点在X左侧,说明y不在i的最长回文字符串中,此时Rm-i+1 < f(j), 根据i和j的对应关系,我们最多可知f(i) 的长度至少为 Rm -i +1,因此把f(i)初始化为Rm-i+1。

  • -y–X----j--------Im---------i------Rm—

  • 综合case1 和case2, f(i)的长度最大不应该超过Rm-i+1, 因此f(i) = min(f(j), Rm -i +1);)
    如果 i>r m,那就先初始化 f(i) = 1。

中心拓展

  • 做完初始化之后,我们可以保证此时的 s[i + f(i) - 1] = s[i - f(i) + 1],要继续拓展这个区间,我们就要继续判断 s[i + f(i)]和 s[i - f(i)] 是否相等,如果相等将 f(i)自增;这样循环直到 s[i+f(i)] =s[i−f(i)],以此类推。我们可以看出循环每次结束时都能保证 s[i + f(i) - 1] = s[i - f(i) + 1],而循环继续(即可拓展的条件)一定是 s[i + f(i)] = s[i - f(i)]。 这个时候我们需要注意的是不能让下标越界,有一个很简单的办法,就是在开头加一个 $,并在结尾加一个 !,这样开头和结尾的两个字符一定不相等,循环就可以在这里终止。

这样我们可以得到 s 所有点为中心的最大回文半径,也就能够得到 S 中所有可能的回文中心的的最大回文半径,把它们累加就可以得到答案。

转载:力扣官方题解

你可能感兴趣的:(每日一题)