manacher算法

manacher算法是用来解决最长回文子串的。其中有一道题目是GDOI2015Day1的第一题。那么,manacher算法又是怎样的呢?

首先来看一看最长回文子串有怎样的解法:

O(n^3):枚举串的起止点,判断回文。

O(n^2):枚举中间点,扩展下去。

O(n):manacher算法,可以粗略地理解为O(n^2)的扩展。

首先,回文子串的长度,有时候是奇数,而有时候是偶数,为了统一——把偶数变成奇数——我们只需要在字符中间和开头结尾加上#号。

然后,我们设f[i]为以i为中心的最长回文子串的扩展长度,那这样,我们就要用O(n)的时间来求出所有的f[i]。怎么求出呢?

我们可以从f[1~i-1]中得到某些信息来推导f[i],从而减少O(n^2)算法的扩展次数。为了拿到更多的信息,需要把之前最大的f(i)值的i记为id,mx = id + f[id]。然后,再利用f[2id-i]得出答案。

这样,当mx>i时,我们可以得出 f[i] = min(f[2*id-i] , mx-i)。这里就是算法的重点了。为何是f[2*id-i]呢?

举个例子:

            

     id    i    mx
 012345678
sababcbaba
f1331913  

当求到i时,我们可以发现:f[2*id-i]值与f[1]值相同。而1 = 2 * id - i,所以,我们可以这样来推导f[i]而无需再扩展(得到答案)。

而当是部分答案时,则是mx - i,再扩展f[i]。

其他:f[i] = 1,扩展。


最后,我们得到一个几乎是线性时间的算法,这就是manacher算法。

【程序】

#include <cstdio>
#include <string>
#include <cstring> 
#include <iostream>

using namespace std;

string s;

void init()
{
	string st;
	cin>>st;
	
	s = "";
	int len = st.size();
	//在开头结尾与字符之间加上'#'号 
	s += "$#";
	for(int i=0;i<len;i++)
	{
		s += st[i];
		s += "#";
	}
	return ;
}

void manacher(string s)//
{
	int f[100010];
	memset(f,0,sizeof(f));
	
	int id = 0 , mx = 0 , len = s.size();
	
	for(int i=0;i<len;i++)
	{
		if(mx > i)
			f[i] = min(f[id*2 -i] , mx - i);//得到答案和得到部分答案。 
		else
			f[i] = 1;
		
		while(s[i - f[i]] == s[i + f[i]]) f[i] ++;//扩展 
		if(i + f[i] > mx)//维护id
		{
			mx = i + f[i];
			id = i;
		}
	}
	
	int maxn = -1 , j = 0;
	
	for(int i=0;i<len;i++)//寻找最大回文子串 
	if(f[i] > maxn)
	{
		maxn = f[i];
		j = i;
	}
	
	maxn -- ;
	
	int st = j - maxn, en = j + maxn;
	for(int i=st;i<=en;i++)//输出 
	if(s[i]!='#')
	{
		cout<<s[i];
	}
	
	return ;
}

int main()
{
	init();
	manacher(s);
	return 0;
}


转载于:https://www.cnblogs.com/ouqingliang/p/9245284.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值