Manacher算法

最近发生了很多事。博客本来准备用自己搭(想要看的可以访问www.wilverain.com)的,后来发现不管是wordpress还是hexo都不是很喜欢,于是就又回到csdn了。等以后成为全栈再自己写一个吧(划掉


最近开始刷hihocoder里面的题目了,就是跟着hiho一下里面来做,预期是每天做一题。毕竟已经是退役狗了。所以对于每一个hihocoder应该都会写一个博客来记录自己的思路或者学习的过程。


嘛回到正题。


提到最长回文子串,最简单的思路,就是暴力枚举每一个子串,然后判断该子串是否是回文串,但是这样的效率,仅枚举每一个子串就已经达到了O(n^2),在加上判断是否是回文串,其复杂度已经达到O(n^3),这样的效率是非常不可取的。


那么就有了相应的优化,对于每一个回文串,其关于对称中心应该是对称的,那么对于一个字符串,只需要枚举每一个对称中心,即可查找到该对称中心的最常回文子串。时间复杂度为O(n^2)


而Manacher算法更加高效,它能够在O(n)的时间复杂度下找到最长回文字串。


首先介绍下Manacher算法需要用到的数据结构吧。

1.p数组,p[i]表示以i为对称中心所能扩展到的最远的长度,包括str[i]

2.mx,表示在i之前的所有以j(j < i)为对称中心的回文串所能扩展到的最远的距离

3.id,表示当最远距离为mx时,其对称中心为id

数据结构主要就这些,我们用例子来说明Manacher算法吧。如abbacdca


首先,因为最长回文字串可以是奇数长度也可以是偶数长度,而这个过程却经常需要去判断,所以为了避免这个过程,我们将待求的串都变成奇数长度的串。即,在两个字符之间添加'#'字符(也可以是别的字符,只要是原串未出现的字符均可以),那么可以得到新的串:#a#b#b#a#c#d#c#a#。当然也可以再将这个字符串扩展一下,以避免在计算的过程中出现需要判断是否越界的情况。(这里在代码中体现)


得到了这个字符串之后,先去判断mx与当前的i的关系。如果mx <= i,那么我们对于p[i]无能为力,无法直接得到p[i]的准确值,所以将p[i]赋值为1;如果mx > i,那么又有两种情况。


因为mx > i的,那么很明显,以id为对称中心的回文串包含了i,那么其对称点就为j = 2 * id - i;

第一种情况,以j为对称中心的回文子串完全包含于以id为对称中心的回文子串,那么显然我们可以得到p[i] = p[j]

第二种情况,以j为对称中心的回文子串并没有完全包含于以id为对称中心的回文子串,这个时候,p[i] >= mx - i;利用回文串对称的原理很容易得到这个结果。

将这两种情况合并,于是我们可以得到p[i] >= min(mx - i, p[2 * id - i]);


根据这两种情况,我们来计算下刚刚得到的新串的每个元素的p[i]值

# a # b # b # a # c # d # c # a #

1 2 1 2 4 2 1 2 1 2 1 6 1 2 1 2 1

根据我们的观察可以发现,abbaacdca这个字符串的最长回文子串长度显然是5,于是我们可以得到一个结论,max(p[i]) - 1就是最长回文子串的长度。

于是就求出了这个过程。


以下是Manacher的代码:

public static int Manacher( String str ) {
	int cur = 0, ans = 0, len = str.length();
	char[] tmp = new char[2 * len + 1];
		
	tmp[cur++] = '#';
	for( int i = 0; i < len; ++i ) {
		tmp[cur++] = str.charAt(i);
		tmp[cur++] = '#';
	}
	
	int mx = 0, id = 0;
	int[] p = new int[ cur ];
		
	for( int i = 0; i < cur; ++i ) {
		p[i] = (mx > i ? Math.min( p[2 * id - i] , mx - i) : 1);
		while( ( i + p[i] < cur && (i - p[i] >= 0)) && (tmp[i + p[i]] == tmp[i - p[i]]) ) ++p[i];
		if( i + p[i] > mx ) {
			mx = i + p[i];
			id = i;
		}
		ans = Math.max(ans,  p[i] - 1);
	}
	return ans;
}

附上 hihocoder-1032-最长回文子串的ac代码:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {
	public static int Manacher( String str ) {
		int cur = 0, ans = 0, len = str.length();
		char[] tmp = new char[2 * len + 1];
		
		tmp[cur++] = '#';
		for( int i = 0; i < len; ++i ) {
			tmp[cur++] = str.charAt(i);
			tmp[cur++] = '#';
		}
		
		int mx = 0, id = 0;
		int[] p = new int[ cur ];
		
		for( int i = 0; i < cur; ++i ) {
			p[i] = (mx > i ? Math.min( p[2 * id - i] , mx - i) : 1);
			while( ( i + p[i] < cur && (i - p[i] >= 0)) && (tmp[i + p[i]] == tmp[i - p[i]]) ) ++p[i];
			if( i + p[i] > mx ) {
				mx = i + p[i];
				id = i;
			}
			ans = Math.max(ans,  p[i] - 1);
		}
		return ans;
	}
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ), 1 << 16 );
		BufferedWriter writer = new BufferedWriter( new OutputStreamWriter( System.out ), 1 << 16 );
		
		int n = Integer.parseInt(reader.readLine());
		for ( int i = 0; i < n; ++i ) {
			String str = reader.readLine();
			int ans = Manacher(str);
			writer.write(ans + "\r\n");
			writer.flush();
		}
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值