• manacher算法


    manacher,又叫马拉车。

    马拉车可以用来求最长回文子串。

    思考如何求最长回文子串,我们首先会想到一个 (O(n^3)) 的解法:枚举所有子串,然后判断是否为回文串。当然这个解法非常大 蠢。
    然后,我们又会有一个想法,枚举每个点,然后从这个点向外拓展,如果相同则拓展,否则记录答案。这里有个问题,就是回文串的长度可能是偶数,也就是没有中间点,那么我们可以在每两个字符之间添加 '#',这样就有了中间点了,像这样:

    注意,前面的 '@' 是防止数组越界的,如果扫到头尾都是回文串,那么如果继续扫下去就会越界,而这里有一个 '@' 就可以防止越界的发生。
    那么此时这个回文串真正的长度应该为 半径(包含中间点)-1。
    这个算法的时间复杂度是 (O(n^2))的。
    我们思考上面那个算法,发现他有许多位置是重复枚举的,比如:

    橙色线中间部分是一个回文串,红色线中间部分也是一个回文串,圈起来的是回文串的中心。
    可以发现,这两个回文串会有重复的地方,那么有没有方法避免这些重复枚举呢?答案是有的。
    首先,我们记录一个最大的 (r) 和对应的 (mid),使得 (mid - (r - mid))~(r) 是一个回文串。(这里的 (mid)其实就是回文串中心的位置啦)
    然后,若我们当前查询的节点 (now)(r) 之内:

    那么,既然在一个回文串的右半部分(因为是从前往后扫,所以一定在 (mid) 右边,是有半部分),则一定有一个对应的左半部分的字符(下面简称 (lc) QwQ):

    (lc) 的半径是已经求出的,若 (lc) 的半径是在这个大回文串之内,那么 (now) 半径就和 (lc) 的一样。但如果以 (lc) 为中心的字符串不全在大回文串之中呢?比如:

    那么,此时我们能确定的半径就是 (r-now),也就是这一段:

    那么,我们只要在上面两个中取一个最小值即可。

    code:

    #include<cstdio>
    #include<string>
    using namespace std;
    string s;
    int mid,r,ans,b[22000005];//b[i]记录以i为中心的回文串的半径
    int min(int x,int y){return x<y?x:y;}
    void read(string &ss)
    {
    	ss="@#";
    	char ch=getchar();
    	while(ch<'a'||ch>'z') ch=getchar();
    	while(ch>='a'&&ch<='z') ss+=ch,ss+='#',ch=getchar();
    	return ;
    }
    int main()
    {
    	read(s);
    	for(int i=1;i<(int)s.length();i++)
    	{
    		if(i<=r) b[i]=min(b[mid*2-i],r-i+1);
    		while(s[i-b[i]]==s[i+b[i]]) b[i]++;
    		if(i+b[i]-1>r) r=i+b[i]-1,mid=i;//更新r和mid
    		if(b[i]-1>ans) ans=b[i]-1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    MyEclipse 中引用包的快捷键
    php中函数 isset(), empty(), is_null() 的区别
    Mysql sql_mode设置 timestamp default 00000000 00:00:00 创建表失败处理
    20190729 将博客搬至CSDN
    Python网络爬虫精要
    PhpStorm 头部注释、类注释和函数注释的设置
    asp.net 将内容导出EXCEL文件(转)
    C#中用GDI+生成饼状图和柱状图
    企业级架构、开发在软件中的定义
    C#中的装箱与拆箱的概念
  • 原文地址:https://www.cnblogs.com/mk-oi/p/13623618.html
Copyright © 2020-2023  润新知