manacher算法是处理回文子串的一种经典算法。处理回文子串一般使用暴力匹配,动态规划,中心扩散,以及manachar。
在写manacher算法前我们先了解一下中心扩散以及动态规划。暴力匹配比较简单就不再阐述了。

中心扩散法

中心扩散法,顾名思义就是以某一个位置为中心,向周围扩散,直到满足条件或到达边界。中心扩散法的思路是:遍历每一个索引,以这个索引为中心,利用“回文串”中心对称的特点,往两边扩散,看最多能扩散多远。
代码实现:

 public String longestPalindrome1(String s) {//特判if (s == null || s.length() == 0) {return "";}int strLen = s.length();//字符串长度int len = 1;//特判未返回说明回文子串长度至少为1int maxStart = 0;//记录最大回文子串的起始位置索引int maxLen = 0;//记录最大回文子串的结束位置索引for (int i = 0; i < strLen; i++) {int left = i - 1;//左边界int right = i + 1;//右边界//首先往左寻找与当期位置相同的字符,直到遇到不相等为止。while (left >= 0 && s.charAt(left) == s.charAt(i)) {len++;left--;}//然后往右寻找与当期位置相同的字符,直到遇到不相等为止。while (right < strLen && s.charAt(right) == s.charAt(i)) {len++;right++;}//最后左右双向扩散,直到左和右不相等。while (left >= 0 && right < strLen && s.charAt(right) == s.charAt(left)) {len = len + 2;left--;right++;}//如果len大于maxlen就更新maxlenif (len > maxLen) {maxLen = len;maxStart = left;}len = 1;}//maxStart + 1:因为在执行时向左扩散是直到不相等才停止,所以最长回文子串并不包括索引为maxStart的字符//maxStart + maxLen + 1:是因为substring()方法是左闭右开区间return s.substring(maxStart + 1, maxStart + maxLen + 1);}

中心扩散时间复杂度为O(n²);
空间复杂度为O(1);

动态规划

对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串 “ababa”,如果我们已经知道 “bab” 是回文串,那么 “ababa” 一定是回文串,这是因为它的首尾两个字母都是 ‘a’。
根据这样的思路,我们就可以用动态规划的方法解决本题。我们用 s[i,j] 表示字符串 s 的第 i 到 j 个字母组成的串是否为回文串,二对于不是回文子串又有两种情况:
①:s[i,j] 本身不是一个回文串;
②: i>j,此时 s[i, j] 本身不合法。
也就是说,只有 s[i+1:j-1]是回文串,并且 s 的第 i 和 j 个字母相同时,s[i:j] 才会是回文串。上文的所有讨论是建立在子串长度大于 2 的前提之上的,我们还需要考虑动态规划中的边界条件,即子串的长度为 1 或 2。对于长度为 1 的子串,它显然是个回文串;对于长度为 2 的子串,只要它的两个字母相同,它就是一个回文串。

public String longestPalindrome(String s) {int len = s.length();if (len < 2) {return s;}int maxLen = 1;int begin = 0;// dp[i][j] 表示 s[i..j] 是否是回文串boolean[][] dp = new boolean[len][len];// 初始化:所有长度为 1 的子串都是回文串for (int i = 0; i < len; i++) {dp[i][i] = true;}char[] charArray = s.toCharArray();// 递推开始// 先枚举子串长度for (int L = 2; L <= len; L++) {// 枚举左边界,左边界的上限设置可以宽松一些for (int i = 0; i < len; i++) {// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得int j = L + i - 1;// 如果右边界越界,就可以退出当前循环if (j >= len) {break;}//字符不相等显然不是回文串if (charArray[i] != charArray[j]) {dp[i][j] = false;} else {//字符相等且长度小于3时if (j - i < 3) {dp[i][j] = true;} else {dp[i][j] = dp[i + 1][j - 1];}}// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置if (dp[i][j] && j - i + 1 > maxLen) {maxLen = j - i + 1;begin = i;}}}return s.substring(begin, begin + maxLen);}

动态规划时间复杂度为O(n²);
空间复杂度为O(n²);

了解了中心扩散和动态规划之后,我们来手撕Manacher!!!

为了表述方便,我们定义一个新概念臂长(也叫回文半径,也可以理解成是中心扩散的步数),表示中心扩展算法向外扩展的长度。如果一个位置的最大回文字符串长度为 2 * length + 1 ,其臂长为 length。例如“abcba”,字符‘c’最大回文串长度是2*2+1=5,其臂长就是2.
在中心扩散中我们可以得到每一位置的臂长,但在中心扩散法中并没有取利用已知的臂长,而Manacher是怎样利用的呢?
我们用一个变量来记录当前已经出现的回文串的 “最” 右边的索引(maxRight)center则表示这个回文串的中心位置,以及当前位置相对于center对称的位置mirror:

①:包含关系:即当前位置索引小于当前已经出现的回文串的 “最” 右边的索引(maxRight)并且当前位置到回文串的最右侧索引的距离比当前位置关于中心的对称位置的中心扩散的臂长还大(i<maxRight&&maxRight-i>arr[mirror]);
如图所示:

处理方法:
直接返回当前位置关于中心点的对称位置的臂长(arr[i] = arr[mirror])
②:相交,即当前位置索引小于当前已经出现的回文串的 “最” 右边的索引(maxRight)并且当前位置到回文串的最右侧索引的距离小于等于当前位置关于中心的对称位置的中心扩散的臂长(i<maxRight&&maxRight-i<=arr[mirror]);
如图所示:

处理方法:返回maxRight-i和arr[mirror]的较小值,(arr[i]=Math.min(maxRight-i,arr[mirror]);)
③:新起点,即当前位置以及是回文串的边界或已经越过边界(maxRight<=i )
如图所示:

处理方法:进行中心扩散

还有一个字符串长度奇偶数的问题该怎么解决呢?

我们可以对字符串进行预处理,在原字符串的前后各加一个特殊字符(比如我的代码实现使用的是‘@’),然后再在原字符串的每个字符间插入这个特殊字符。当字符长度为奇数时。设为2n+1 处理后则是 (2n+1)2+1 结果是奇数;当字符长度为偶数时。设为2n 处理后则是 (2*n)*2+1 结果依然是奇数;这样就解决了字符串长度的奇偶数问题。

代码实现:

public String longestPalindrome(String s) {int len=s.length()*2+1;/*对字符串进行预处理(字符串前后有一个'@'字符,每个字符中间加一个'@'字符,假设原字符串长度为2n(偶数时),处理后字符串长度为4n+1假设原字符串长度为2n-1(奇数时),处理后字符串长度为4n-1也就是无论原字符串长度是奇数还是偶数都可以解决也就是s.length()*2+1,解决字符串单偶问题)*/char[] str =new char[len];str[0]='@';for(int i=1;i<len;i++){//奇数时写入字符串s[i/2](因为字符数组读入是读入一个字符再读入一个'@',中间有1跨度)//偶数时写入'@'str[i]=i%2==1?s.charAt(i/2):'@';}//定义数组arr,arr每个元素是字符串对应下标中心扩散的步数,也可以叫做回文半径int[] arr= new int[len];arr[0]=0;//arr[0]左边就是边界无法进行中心扩散,所以步数为0//回文串的中心centre,回文串最右边下标(已经出现的回文串的 “最” 右边的下标)maxRight//遍历字符数组时,i关于中心centre对称的点mirror,   mirror=2*center-i(因为center=(mirror+i)/2)int center=0,maxRight=0,mirror=0;//start 最长回文子串起始位置  ,maxlen最长回文子串的长度int start=0,maxLen=1;//循环遍历字符数组,并且更新center以及maxRightfor(int i=1;i<len;i++){//这一步结合了相容和相交的情况,不去细分两者,而是取两者的共同之处,arr[i]先取当前位置到回文串最右侧的距离与当前位置的对称点的臂长的较小值,然后尝试向两边扩散if(i<maxRight){mirror=2*center-i;arr[i]=Math.min(maxRight-i,arr[mirror]);}//尝试进行中心扩散int left=i-(arr[i]+1);int right=i+(arr[i]+1);while(left >= 0 && right < len && str[left] == str[right]){left--;right++;arr[i]++;}//更新maxRight以及centerif(i+arr[i]>maxRight){maxRight=i+arr[i];center=i;}if (arr[i] > maxLen) {// 记录最长回文子串的长度和相应它在原始字符串中的起点maxLen = arr[i];start = (i - maxLen) / 2;}}return s.substring(start,start+maxLen);}

Manacher算法时间复杂度为O(n);
空间复杂度为O(n);
如有错误欢迎指出,共同进步

数据结构与算法---Manacher算法相关推荐

  1. 常考数据结构与算法-manacher算法

    回文字符串123321 回文直径:6 回文半径:3 求一个字符串中的最长回文字符串. abc123321def 一般会在原字符串中添加特殊字符,比如添加"#". 变成#a#b#c# ...

  2. 通俗易懂的最长回文串图解、说明及Java代码(中心扩散法和Manacher算法)

    1. 回文串 作为程序员,回文串这个词已经见怪不怪了,就是一个字符串正着读和反着读是一样的,形式如abcdcba.bbaabb.这里涉及到奇回文和偶回文,奇回文指回文串的字符数是奇数,偶回文指回文串的 ...

  3. java 最长回文串_通俗易懂的最长回文串图解、说明及Java代码(中心扩散法和Manacher算法)...

    1. 回文串 作为程序员,回文串这个词已经见怪不怪了,就是一个字符串正着读和反着读是一样的,形式如abcdcba.bbaabb.这里涉及到奇回文和偶回文,奇回文指回文串的字符数是奇数,偶回文指回文串的 ...

  4. manacher算法详解(马拉车算法)

    马拉车算法 Manacher算法是由题目"求字符串中最长回文子串的长度"而来.比如 abcdcb 的最长回文子串为 bcdcb ,其长度为5. 回文:正着念,反着念都一样 暴力解不 ...

  5. 浅谈Manacher算法与扩展KMP之间的联系

    首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解,网上解法颇多,时间复杂度也不尽相同,这里列述几种常见的解法. 解法一   ...

  6. 数据结构与算法之Manacher算法

    数据结构与算法之Manacher算法 目录 Manacher算法概述 Manacher算法代码实现 扩展题--如果只能向字符串后面添加字符,怎么让整体串变成回文串,要求填的字符最少 1. Manach ...

  7. 【数据结构与算法】Manacher算法

    Manacher算法 https://github.com/SongJianHIT/DataStructurs-Algorithm/tree/main/src/algorithms/manacher ...

  8. 【数据结构】Manacher算法

    Manacher Manacher算法要解决的问题是在一个字符串中寻找最大回文子字符串长度. 该问题的暴力解法是遍历每一个字符,从该字符向两边扩展,寻找边界. 但该方法中,每次扩展得到的信息并不能被充 ...

  9. 【最长回文子串】Manacher算法详解

    写在前面 manacher算法解决最长回文子串以及变形问题的时间复杂度为O(n). 如果你想囫囵吞枣,只需要使用到该算法,你可以直接把代码拿走:但如果你想深入了解这个算法的工作原理和关键部分解读,还是 ...

最新文章

  1. 在流程梳理的过程中,OA工作组应采用国际先进方法
  2. 7种常见的数据分析基本思路,满足你职场95%的需求
  3. vscode vue解决跨域_在vs code 中如何创建一个自己的 Vue 模板代码
  4. 差分进化算法python_差分进化算法DE-python实现
  5. VS2015如何使自己的exe文件在别人的电脑上运行(找不到MSVCP140D.dll)
  6. 高德开放平台发布“GAIA计划”,打造“组件式”解决方案
  7. ASP.NET 备忘
  8. 两个方法教你彻底解决win10系统更新补丁安装失败的问题
  9. sublime text 编辑器批量删除空白行
  10. Excel技能树系列05:TEXT函数,IF函数和INDEX+MATCH组合查找函数
  11. 记阿里巴巴的一次面试,教你怎样应对到来的“金三银四
  12. Easy Data Transform for mac (Excel和CSV编程文件转换工具) v1.11.1激活版
  13. Android实现自适应正方形GridView(陌陌引导页面效果)
  14. 计算机辅助小学数学教学的研究,计算机辅助小学数学教学研究.doc
  15. 读书笔记-尖刀团队特训记
  16. 高级映射(一):一对一、一对多,多对多查询总结
  17. android勒索软件,安卓勒索软件最新伎俩
  18. MariaDB 分区
  19. 创客教育中的空间设计实物原理
  20. 高性能web平台【OpenResty入门与实战】

热门文章

  1. 商淘软件多用户商城系统:助您轻松搭建B2B2C商城
  2. 极大似然估计,最大后验估计的区别
  3. 邮政邮件+邮政邮戳,电子邮件+电子邮戳
  4. nginx 最简单的虚拟主机配置
  5. TryHackMe-Bypass_Disable_Functions
  6. org.springframework.http.converter.HttpMessageNotReadableException异常解决
  7. 读小王子系列2《风沙星辰》有感
  8. java毕设项目开发 基于SSM库存物资管理系统 仓库物资管理系统 物资库存管理系统Java
  9. mysql cluste 5.6.29 ndb-7.4.11搭建实战
  10. 低功耗设计:如何计算power switch的数量?