date: 2024-06-22
title: Manacher Algorithm
status: DONE
dg-publish: true
author:
  - AllenYGY
tags:
  - Manacher
  - String
  - NOTE
  - Algorithm
created: 2024-06-22T00:52
updated: 2024-06-23T16:15Manacher Algorithm
给定一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
提示:
1 <= s.length <= 1000s 仅由数字和英文字母组成

回文覆盖最右边界r、回文中心c


假设一个字符串 s, 字符串s的 Manacher扩展串为 p,p[i] 表示当前字符可以扩展的最长回文串的长度
现在假设我们要对下一个 
如果 
如果 

2*c-i 的回文半径 s[2*c-i],在大回文区域以外,直接确定


当前字符最长回文串的实际长度 

实际回文串终止位置 = 扩展回文串结尾下标 / 2

string Preprocess(string s) { // 字符串预处理
	string res = "#"; //在每个字符前后添加扩展字符
	for (int i = 0; i < s.size(); i++) {
		res += s[i];
		res += "#";	
	}
	return res;
}
string Manacher(string s) {
	string t = Preprocess(s);
	string ans = "";
	vector<int> p(t.size(), 0);
	int mx = 0;
	for(int i = 0, c = 0, r = 0, len; i < t.size(); i++) {
		// 当 i < r 时,
			// 若此时 i + s[i] 在 r 内, 则 r - i >= p[2 * c - i]
			// len = p[2 * c - i]
			// 若此时 i + s[i] 在 r 外, 则 r - i <= p[2 * c - i]
			// len = r - i
		// 当 i >= r 时
			// 回文字符串即为 i 本身,len = 1
		len = i < r ? min(r - i, p[2 * c - i]) : 1; 
		
		while (i + len < t.size() && i - len >= 0 && t[i + len] == t[i - len]){	// 不管是否可以扩展,都尝试扩展
			len++;
		}
		if (i + len > r){ //更新新的回文中心和回文半径
			r = i + len;
			c = i;
		}
		if(len > mx){ // 维护最长回文子字符串
			mx = len;
			ans = s.substr((i - len + 1)/2, len - 1);
		}
		p[i] = len;
	}
	return ans;
}
返回字符串s的回文子串数量
给定一个字符串str和一个正数k
你可以随意把str切分成多个子串
目的是找到某一种划分方案,有尽可能多的回文子串
并且每个回文子串都要求长度>=k、且彼此没有重合的部分
返回最多能划分出几个这样的回文子串
长度前k名的奇数长度回文子串长度乘积
给定一个字符串s和数值k,只关心所有奇数长度的回文子串
返回其中长度前k名的回文子串的长度乘积是多少
如果奇数长度的回文子串个数不够k个,返回-1
最长双回文串长度
输入字符串s,求s的最长双回文子串t的长度
双回文子串就是可以分成两个回文串的字符串
比如"aabb",可以分成"aa"、"bb"