回文子串

647. 回文子串 - 力扣(LeetCode) (leetcode-cn.com)

方法1:中心扩展--遍历中心/遍历中心种类

  假设字符串长度n=4。

 

  回文左起始位置 回文右起始位置
0 0 0
1 0 1
2 1 1
3 1 2
4 2 2
5 2 3
6 3 3

可以看出中心种类有7中,可得公式s=2*n-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: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];
  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;//rMax-i+1防止f[2*iMax-i]过大超过右边界
    // 中心拓展
    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,所以除二即可
    ans += f[i] / 2;
  }

  return ans;
}

时间复杂度:O(n))。即 Manacher 算法的时间复杂度,由于最大回文右端点 r_mrm只会增加而不会减少,故中心拓展进行的次数最多为 O(n)O(n),此外我们只会遍历字符串一次,故总复杂度为 O(n)O(n)。

空间复杂度:O(n)。

posted @ 2022-03-28 14:43  hhsfff  阅读(66)  评论(0)    收藏  举报