最长回文字符串(manacher算法)
偶然看见了人家的博客发现这么一个问题,研究了一下午, 才发现其中的奥妙。Stupid。
- 题目描述:
- 回文串就是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。
- 回文子串,顾名思义,即字符串中满足回文性质的子串。
- 给出一个只由小写英文字符a,b,c...x,y,z组成的字符串,请输出其中最长的回文子串的长度。
- 输入:
- 输入包含多个测试用例,每组测试用例输入一行由小写英文字符a,b,c...x,y,z组成的字符串,字符串的长度不大于200000。
- 输出:
- 对于每组测试用例,输出一个整数,表示该组测试用例的字符串中所包含的的最长回文子串的长度。
- 样例输入:
-
abab
bbbb
abba
- 样例输出:
-
3
4
4
思路:
回文串包括奇数长的和偶数长的,一般求的时候都要分情况讨论,这个算法做了个简单的处理把奇偶情况统一了。原来是奇数长度还是奇数长度,偶数长度还是偶数长度。
算法的基本思路是这样的,把原串每个字符中间用一个串中没出现过的字符分隔#开来(统一奇偶),同时为了防止越界,在字符串的首部也加入一个特殊符$,但是与分隔符不同。同时字符串的末尾也加入'\0'.
算法的核心:用辅助数组p记录以每个字符为核心的最长回文字符串半径。也就是p[i]记录了以str[i]为中心的最长回文字符串半径。p[i]最小为1,此时回文字符串就是字符串本身。
先看个例子:
原串: w aa bwsw f d
新串: $ # w# a # a # b# w # s # w # f # d #
辅助数组P: 1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1
首先看代码(借助http://blog.csdn.net/thyftguhfyguj/article/details/9531149):
#include <stdio.h>
#include <iostream>
using namespace std; char s[200002];
char str[400010];
int p[400010]; int min(int a,int b){
return a < b ? a : b;
} int pre(){
int i,j = 0;
str[j++] = '$';//加入字符串首部的字符串
for(i = 0;s[i];i++){
str[j++] = '#'; //分隔符
str[j++] = s[i];
}
str[j++] = '#';
str[j] = '\0'; //尾部加'\0'
cout<<str<<endl;
return j;
} void manacher(int n){
int mx = 0,id,i;
p[0] = 0;
for(i = 1;i < n;i++){
if(mx > i) //在这个之类可以借助前面算的一部分
p[i] = min(mx - i,p[2 * id - i]); //p[2*id-1]表示j处的回文长度
else //如果i大于mx,则必须重新自己算
p[i] = 1;
while(str[i - p[i]] == str[i + p[i]]) //算出回文字符串的半径
p[i]++;
if(p[i] + i > mx){ //记录目前回文字符串扩展最长的id
mx = p[i] + i;
id = i;
}
}
} int main(int argc, char const *argv[]){ while(scanf("%s",s) != EOF){
int n = pre();
manacher(n);
int ans = 0,i;
for(i = 1;i < n;i++)
if(p[i] > ans)
ans = p[i];
printf("%d\n",ans - 1);
}
return 0;
}
上面的程序说明:pre()函数对给定字符串进行预处理,也就是加分隔符。
上面几个变量说明:id记录具有遍历过程中最长半径的回文字符串中心字符串。mx记录了具有最长回文字符串的右边界。看下面这个图(注意,j为i关于id对称的点,j = 2*id - i):
但是p[i] = p[j]是没有错的,但是这里有个问题,就是i的一部分超出阴影部分,这就不对了。请看下图(为了看得更清楚,下面子串用细条纹表示):
此时,根据对称型只能得出p[i]和p[j]红色阴影部分是相等的,这就为什么有取最小值这个操作:
if(mx > i) //在这个之类可以借助前面算的一部分
p[i] = min(mx - i,p[2 * id - i]);
下面代码就很容易看懂了。
最后遍历一遍p数组,找出最大的p[i]-1就是所求的最长回文字符串长度,下面证明一下:
(1)因为p[i]记录插入分隔符之后的回文字符串半径,注意插入分隔符之后的字符串中的回文字符串肯定是奇数长度,所以以i为中心的回文字符串长度为2*p[i]-1。
例如:bb=>#b#b#
bab=>#b#a#a#b#
(2)注意上面两个例子的关系。#b#b#减去一个#号的长度就是原来的2倍。即((2*p[i]-1)-1)/2 = p(i)-1,得证。
算法的有效比较次数为MaxId 次,所以说这个算法的时间复杂度为O(n)。
最长回文字符串(manacher算法)的更多相关文章
- 【转载】最长回文字符串(manacher算法)
原文转载自:http://blog.csdn.net/lsjseu/article/details/9990539 偶然看见了人家的博客发现这么一个问题,研究了一下午, 才发现其中的奥妙.Stupid ...
- 第5题 查找字符串中的最长回文字符串---Manacher算法
转载:https://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一 ...
- 九度OJ 1528 最长回文子串 -- Manacher算法
题目地址:http://ac.jobdu.com/problem.php?pid=1528 题目描述: 回文串就是一个正读和反读都一样的字符串,比如"level"或者"n ...
- lintcode最长回文子串(Manacher算法)
题目来自lintcode, 链接:http://www.lintcode.com/zh-cn/problem/longest-palindromic-substring/ 最长回文子串 给出一个字符串 ...
- 最长回文子串—Manacher 算法 及 python实现
最长回文子串问题:给定一个字符串,求它的最长回文子串长度.如果一个字符串正着读和反着读是一样的,那它就是回文串. 给定一个字符串,求它最长的回文子串长度,例如输入字符串'35534321',它的最 ...
- hihocoder #1032 : 最长回文子串 Manacher算法
题目链接: https://hihocoder.com/problemset/problem/1032?sid=868170 最长回文子串 时间限制:1000ms内存限制:64MB 问题描述 小Hi和 ...
- 5. Longest Palindromic Substring(最长回文子串 manacher 算法/ DP动态规划)
Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...
- hihoCoder #1032 : 最长回文子串 [ Manacher算法--O(n)回文子串算法 ]
传送门 #1032 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相 ...
- HDU 3068:最长回文(Manacher算法)
http://acm.hdu.edu.cn/showproblem.php?pid=3068 最长回文 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的 ...
随机推荐
- SuperSocket快速入门(一):什么是SuperSocket
什么是SuperSocket SuperSocket(下文简称SS)是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架.你无须了解如何使用 Socket, 如何维护 ...
- 织梦DEDECMS {dede:field name='position'/}标签增加其它属性的
在默认情况下,织梦(DedeCms)系统当前位置的调用标签为: {dede:field name='position'/} 在这种默认的情况下,生成后的代码大致为如下格式: 主页 > 应用软件 ...
- 基于jQuery仿uploadify的HTML5图片上传控件jquery.html5uploader
(function($){ var methods = { init:function(options){ return this.each(function(){ var $this = $(thi ...
- Installing node-oracledb on Microsoft Windows
版本 7 由 Laura Ramsey-Oracle 于 2015-10-19 下午11:46创建,最后由 cj 于 2015-10-22 下午7:44修改. Installing node-orac ...
- 升级openssl到1.0.1g
先进行支撑包的安装: # yum install -y zlib openssl升级步骤: 下载最新版本的openssl源码包 # wget ftp://ftp.openssl.org/sourc ...
- 武汉科技大学ACM :1004: A+B for Input-Output Practice (IV)
Problem Description Your task is to Calculate the sum of some integers. Input Input contains multipl ...
- Java学习笔记——SequenceInputStream类合并文件的综合举例分析
SequenceInputStream 介绍 SequenceInputStream 类表示其他输入流的逻辑串联,即文件的合并. 它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾, ...
- PHP微信红包的算法实现探讨
header("Content-Type: text/html;charset=utf-8");//输出不乱码,你懂的 $total=10;//红包总额 $num=8;// 分成8 ...
- 视图必须派生自 WebViewPage 或 WebViewPage错误解决方法
1,在每个视图上面添加 @inherits System.Web.Mvc.WebViewPage 2,将views中的web.config COPY到新的视图模版文件夹下,就可以了
- 利用libpcap抓取QQ号码信息
最近想在QQ登录时把QQ号码信息记录下来,百度了很多都没有找到具体方式,最近用Wireshark分析报文+libpcap库嗅探实现了这个小功能. 通讯背景: QQ客户端在通讯时使用UDP协议,其中数据 ...