• Manacher 学习


    先来一段代码压压惊:

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 1e5+5;
    
    char str[maxn],str2[maxn*2];
    int Len[maxn];
    
    int string_init() {
    	str2[0] = '$';
    	str2[1] = '#';
    	int len = 2;
    	int length = strlen(str+1);
    	for (int i = 1;i<=length;i++) {
    		str2[len++] = str[i];
    		str2[len++] = '#';
    	}
    	str2[len-1] = '$';
    	str2[len] = '';
    	return len;
    }
    
    int Manacher() {
    	int len = string_init();
    	int mxlen = -1,maxr = -1,middle = -1;
    	for (int i = 1;i < len;i++) {
    		if ( maxr < i ) {
    			int j = 1,cnt = 1;
    			while ( str2[i-j] == str2[i+j] ) j++,cnt += 2;
    			Len[i] = cnt; middle = i; maxr = i + j;
    			mxlen = max(mxlen,Len[i]);
    		} else if ( maxr > i && Len[2*middle-i] == (maxr-i) ) {
    			int j = maxr - i,cnt = Len[2*middle-i];
    			while ( str2[i-j] == str2[i+j] ) j++,cnt+=2;
    			Len[i] = cnt; middle = i; maxr = i + j;
    			mxlen = max(mxlen,Len[i]);
    		} else if ( maxr >= i ) {
    			Len[i] = min(Len[2*middle-i],maxr-i);
    		}
    	}
    	return mxlen;
    }
    
    int main() {
    	while(~scanf("%s",str+1)){
    		printf("%d
    ",(Manacher()+1)/2);
    	}
    	return 0;
    }
    
    

    想像一下对于一个字符串,如何求它的最长的回文子串,暴力的做法就是枚举每一个点然后往两边扩,但是这个算法的时间复杂度是O(n2)O(n^2)O(n2)时间很不进人意,于是就诞生了Manacher算法

    Manacher

    开始Manacher之前我们首先要准备两个变量maxr和middle ,maxr 和 middle 依次表示 回文串的最大右边界,和 最大右边界的回文串的中心

    (1).

    如果maxr小于当前要查找的回文中心,我们就暴力寻找(这一点可以参见,我的Manacher函数中的第一个if)

    if ( maxr < i ) {
    		int j = 1,cnt = 1;
    		while ( str2[i-j] == str2[i+j] ) j++,cnt += 2;
    		Len[i] = cnt; middle = i; maxr = i + j;
    		mxlen = max(mxlen,Len[i]);
    }
    
    (2).

    这里写图片描述
    请看图片如果当前要寻找的位置 iii,在maxr里面我们可以在回文的对称一边找到 i′i&#x27;i 并且如果i′i&#x27;i的回文串左边界比maxl大的话,那么我当前位置iii的回文子串的长度就跟i′i&#x27;i的一样

    else if ( maxr >= i ) { // 为什么是min(Len[2*middle-i],maxr-i);呢往下看
    	Len[i] = min(Len[2*middle-i],maxr-i);
    }
    
    (3).

    这里写图片描述
    在第二种的基础上,我的i′i&#x27;i的回文串的左边界比maxl要小,但是此时iii的回文串的长度为maxr−imaxr - imaxri 可以证明一下为什么不等于i′i&#x27;i的回文串长度,如果我当前位置的回文子串长度等于i′i&#x27;i的话,那么在maxl和maxr两端的外部是不是还有一段连续相等的串,既然这样的话我的middle得回文串的长度应该更大,可是事实没有更大,因此当前位置的回文子串的长度为maxr−imaxr - imaxri
    ######(4).
    最后一种情况 : 当我的 iii位置的对称点回文左边界刚好与maxl重合
    因为不知道当前位置外面的串的情况,因此对于这种情况暴力寻找回文串

    ###时间复杂度估计
    因为我的maxr不断向右走并且没有重复走的情况等于说我只遍历了一遍串因此时间复杂度为O(n)O(n)O(n)

  • 相关阅读:
    类加载
    LinkedList插入排序实现
    99乘法表
    关于IO流的抽象类
    分解质因数
    Struts2小demo遇到的几个问题
    Tomcat设置欢迎页问题
    数据库迁移
    EF – 1.模式
    正则表达式
  • 原文地址:https://www.cnblogs.com/Nlifea/p/11745953.html
Copyright © 2020-2023  润新知