算法学习-最长回文子串

题目

给定字符串str,若子串s是回文串,称s为str的回文字串。设计算法,计算str的最长回文字串。

枚举所有字串,显然是一种解法,但效率太过低下。


算法解析

  • 因为回文串有奇数和偶数的不同,判断一个串是否是回文串,往往要分开编写,造成代码拖沓。
  • 一个简单的事实:缠度为n的字符串,共有n-1个“邻接”,加上首字符的前面,和末字符的后面,共n+1的“空”。因此,字符串本身和“空”一起,共有2n+1个,必定是奇数。如abbc-〉#a#b#c#;aba-〉#a#b#a#
  • 因此,将带计算母串扩展成gap串,计算回文字串的过程中,只考虑奇数匹配即可。
  • 字符串12212321-〉s[]="$#1#2#2#1#2#3#2#1#";为了处理统一,最前面加一位未出现的字符,如$
  • 用一个数组P[i]来记录以字符S[i]为中心的最长回文字串向左/右扩张的长度(包括S[i]),比如S和P的对应关系:
  • S  # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
  • P  1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
    • P[i]-1正好是原字符串中回文串的总长度

下面介绍一下回文算法的核心算法思想Manacher算法

如下图


  • 这里需要求的是i的回文字串的个数,mx为目前匹配的最远的位置,为0到i-1中k+P[k]最大的数,代码里有体现,现在已知的是i以前的所有回文字串是求出来的。
  • 记i关于id的对称点位j(=2*id-i),若此时满足条件mx-i〉P[j];则有如下:
    • 记my为mx关于id的对称点(my=2*id-mx);
    • 由于以S[id]为中心的最大回文子串为S[my+1...id...mx-1],即:S[my+1,...,id]与S[id,...,mx-1]对称,而i和j关于id对称,因此P[i]=P[j](P[j]是已知的)。

  • 记关于id的对称点为j(=2*id-i),若此时满足条件mx-i〈P[j],则有如下:
    • 记my为mx关于id的对称点(my=2*id-mx);
    • 由于以S[id]为中心的最大回文子串为S[my+1...id...mx-1],即:S[my+1,...,id]与S[id,...,mx-1]对称,而i和j关于id对称,因此P[i]至少等于mx-i(途中绿色框部分)。

代码如下所示

void Manacher(char* s, int* P)
{
	int size = strlen(s);
	P[0] = 1;
	int id = 0;
	int mx = 1;
	for (int i = 1; i < size; i++)
	{
		if (mx > i)//mx比i大,说明i被mx控制住了
		{
			P[i] = P[2*id - i] ? P[2*id - i] < (mx - i);
		}
		else
		{
			P[i] = 1;
		}
		for (; s[i + P[i]] == s[i - P[i]]; P[i]++);
		if (mx < i + P[i])
		{
			mx = i + P[i];
			id = i;
		}
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值