Manacher算法-最长回文子串

一、引言

给定一个字符串 s,找到 s 中最长的回文子串。O(n)时间复杂度。

来源:力扣(LeetCode)

LeetCode-5. 最长回文子串

 

二、Manacher算法

1、该算法通过依据回文子串中心字母考虑,所以首先在源字符串中每个字符间插入特殊符号,拼凑为奇数个字符的串。(包括字符串首尾:方便最终截取答案回文子串)

 

2、该算法流程为单次扫描,每个字符只扫描一次。所以记录如下变量:

(1)maxl:扫描到的最大的右边界下标

(2)maxi:最大右边界所对应的回文子串的中心位置下标

(3)ansi:目前最长回文子串的中心位置下标

 

同时构建数组armlen[],表示以坐标i为中心的回文子串的臂长(无论是否包括自身都可)

3、随后初始化第一个字符后,遍历每个字符,将其作为子串中心点考虑,对第i个字符有以下的情况:

(1)该字符坐标i在最大右边界maxl的右侧:表示未扫描该字符,使用O(n)算法,以中心扩散的形式扫描该回文子串。

(2)该字符坐标i在最大右边界maxl的左侧:则必定存在i关于maxi的对称点symi,判断对称点symi的回文串臂长:

    (i)symi的回文串包含在maxi回文串中,则i的回文串与symi的回文串相同,臂长相同,直接O(1)更新即可。

    (ii)symi的回文串超出maxi回文串的边界,则i的回文串也超出maxi的回文串边界,从maxl开始O(n)中心扩散法扫描i的回文串。

 

4、每遍历一个中心字符后,需要更新maxl和maxi和ansi。

 

核心思想:

该算法特殊之处在于上述流程中的3.(2),当i小于maxl时,i在maxi的回文串中,所以判断对称点symi的情况来简化i的情况。

 

三、程序代码

string longestPalindrome(string s) 
{
	if (s == "")
		return s;

	// 将字符串拼接位奇数个
	string t;
	for (int i = 0; i < s.length(); i++)
	{
		t.push_back('#');
		t.push_back(s[i]);
	}
	t.push_back('#');

	int len = t.length();
	vector<int> armlen(len, 0);	// 以坐标i为中心的回文串的臂长,不算自身

	int maxl = 0;	// 扫描到的最大右边界
	int maxi = 0;	// 最大右边界对应的中心坐标
	int ansi = 0;	// 最长子回文串的中心坐标

	for (int i = 1; i < len; i++)
	{
		if (i >= maxl)	// 该中心点在最长右边界右侧,未进行过扫描,O(n)扫描该回文串
		{
			int l = i - 1;
			int r = i + 1;
			while (r < len && l >= 0 && t[l] == t[r])
			{
				armlen[i]++;
				l--;
				r++;
			}
		}
		else	// 该中心点在最长右边界左侧,进行过扫描,找对称点
		{
			int symi = 2 * maxi - i;	// 对称点坐标
			
			if (armlen[symi] >= maxl - i)	// 边界以内均为回文,需要继续向外扫描判断
			{
				armlen[i] = maxl - i;
				int l = 2 * i - maxl - 1;
				int r = maxl + 1;
				while (r < len && l >= 0 && t[l] == t[r])
				{
					armlen[i]++;
					l--;
					r++;
				}
			}
			else	// 回文串在边界内,直接匹配
			{
				armlen[i] = armlen[symi];
			}
			
		}

		// 维护最大臂长,及答案中心点坐标
		if (i + armlen[i] > maxl)
		{
			maxl = i + armlen[i];
			maxi = i;
		}
		if (armlen[i] > armlen[ansi])
		{
			ansi = i;
		}
	}
	string ans = s.substr((ansi-armlen[ansi])/2, armlen[ansi]);
    return ans;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值