zoukankan      html  css  js  c++  java
  • 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 + "
    ");
    			writer.flush();
    		}
    	}
    }


  • 相关阅读:
    Delphi 与 C/C++ 数据类型对照表
    JAVA中堆和栈的区别
    关于Column '*' not found 解决方案 Hibernate使用SQL查询返回实体类型,即返回某个类,或实体类
    Oracle笔记
    oracle时间运算
    struts2中iterator标签的相关使用
    url传中文,转码
    表格的css,细线表格
    使用struts 2 获取服务器数据 ongl表达式 标签
    struts 2 Commons FileUpload文件上传
  • 原文地址:https://www.cnblogs.com/wiklvrain/p/8179341.html
Copyright © 2011-2022 走看看