回文子串

1.题目描述

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

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

示例 2:

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

2.题解

2.1 中心扩展

public int countSubstrings(String s) {
	int n = s.length(), ans = 0;
	for (int i = 0; i < 2 * n - 1; ++i) {
		int l = i / 2, r = i / 2 + i % 2;
		while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
			--l;
			++r;
			++ans;
		}
	}
	return ans;
}

遍历所有可能的回文中心,累加回文子串的个数。

2.2 Manacher算法

public int countSubstrings(String s) {
	int n = s.length();
	StringBuffer t = new StringBuffer("$#");
	for (int i = 0; i < n; ++i) {
		t.append(s.charAt(i));
		t.append('#');
	}
	n = t.length();
	t.append('!');

	int[] f = new int[n];
	// rMax为最大回文右端点,iMax为最大回文右端点的回文中心
	int iMax = 0, rMax = 0, ans = 0;
	for (int i = 1; i < n; ++i) {
		// 初始化 f[i]
		f[i] = i <= rMax ? Math.min(rMax - i + 1, f[2 * iMax - i]) : 1;
		// 中心拓展
		while (t.charAt(i + f[i]) == t.charAt(i - f[i])) {
			++f[i];
		}
		// 动态维护 iMax 和 rMax
		if (i + f[i] - 1 > rMax) {
			iMax = i;
			rMax = i + f[i] - 1;
		}
		// 统计答案, 当前贡献为 (f[i] - 1) / 2 上取整
		ans += f[i] / 2;
	}

	return ans;
}

Manacher算法的处理方式是在所有的相邻字符中间插入#,比如abaa会被处理成#a#b#a#a#,这样可以保证所有找到的回文串都是奇数长度的。假设原字符串为S,经过这个处理之后的字符串为s
我们用f(i)来表示以s的第i位为回文中心,可以拓展出的最大回文半径。
aaa为例:

注意到f(4)=4,即以s[4]为回文中心的最大回文子串为#a#a#a#,此时,iMax4rMax7
由于5<7,所以f(5)可能为f(3)或者为7-5+1=3。考虑以下代码:

// ...
Math.min(rMax - i + 1, f[2 * iMax - i])
// ...

这里为什么要取较小值呢?以aba为例:

这里f(5)=f(3),因为s[2]=s[6]s[2]≠s[4]s[4]≠s[6]

cabacdcabae为例:

注意到由于s[2]≠s[22],以s[12]为回文中心的最大回文子串为s[3:21]

这里考虑f(18)可能为f(6)或者21-18+1=4
由于s[2]=s[10],以s[6]为回文中心的最大回文子串包含s[2]
如果f(18)=f(6),意味着以s[18]为回文中心的最大回文子串包含s[22],这就要求s[22]=s[14],根据对称性可知,s[10]=s[14],于是得出s[2]=s[22],这显然不对。
因此,当i <= rMax时,f(i)只能取rMax - i + 1f[2 * iMax - i]中的较小值。

最后,ans += f[i] / 2表示累加以第i位为回文中心的回文子串的个数,比如对于$#c#a#b#a#c#,以s[6]为回文中心,其回文子串包含c#a#b#a#ca#b#ab,其回文子串个数为f(6)/2=3
注意:#a#b#a#a#b#a是一样的。

Manacher算法避免了像中心扩展那样盲目地扩展,这里先初始化f[i]再中心扩展。

// ...
while (t.charAt(i + f[i]) == t.charAt(i - f[i])) {
	++f[i];
}
// ...

参考:

posted @ 2020-12-29 16:33  gzhjj  阅读(148)  评论(0编辑  收藏  举报