zoukankan      html  css  js  c++  java
  • 回文子串

    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];
    }
    // ...
    

    参考:

  • 相关阅读:
    iOS的一些面试题分析总结(1)
    iOS的一些面试题分析总结(0)
    iOS页面间传值的一些方式总结
    自定义UIButton
    iOS查看3D效果的手势交互
    关于php得到参数数据
    ios安装ipa与安卓安装apk
    听说程序员想当就能当?
    W5100S、W5500、W5100差异对比
    annot read lifecycle mapping metadata for artifact org.apache.maven.plugins:maven-clean-plugin:maven
  • 原文地址:https://www.cnblogs.com/gzhjj/p/14206432.html
Copyright © 2011-2022 走看看