647.回文子串

647.回文子串

解题思路:

中心扩散法

Manacher 算法

package leadcode;

/**
 * @author : icehill
 * @description : 回文子串
 * 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
 * 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
 * 示例 1:
 * 输入:"abc"
 * 输出:3
 * 解释:三个回文子串: "a", "b", "c"
 * 示例 2:
 * 输入:"aaa"
 * 输出:6
 * 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
 * 提示:
 * 输入的字符串长度不会超过 1000 。
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/palindromic-substrings
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 * @date : 2021-05-28
 */
public class Solution647 {
    public static void main(String[] args) {
        Solution647 solution647 = new Solution647();
        System.out.println(solution647.countSubstrings("aaaa"));
    }

    /**
     * Manacher 算法
     * 时间复杂度:O(n)空间复杂度:O(n)
     * @param s
     * @return
     */
    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];
        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;
    }
    /**
     * 中心扩散法
     * 时间复杂度:O(n^2)空间复杂度:O(1)
     * @param s
     * @return
     */
    public int countSubstringsTwo(String s) {
        int count=0;
        for(int i=0;i<2*s.length()-1;i++){
            // left和right指针和中心点的关系是?
            // 首先是left,有一个很明显的2倍关系的存在,其次是right,可能和left指向同一个(偶数时),也可能往后移动一个(奇数)
            // 大致的关系出来了,可以选择带两个特殊例子进去看看是否满足。
            int left = i / 2;
            int right = left + i % 2;

            while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
                count++;
                left--;
                right++;
            }

        }
        return count;
    }
}