博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【LeetCode-面试算法经典-Java实现】【05-Longest Palindromic Substring(最大回文字符串)】...
阅读量:6328 次
发布时间:2019-06-22

本文共 3453 字,大约阅读时间需要 11 分钟。

背景

近期開始研究算法,于是在leetcode上做算法题,第五题便是关于回文子串的。

什么是回文字串

回文字符串是指将该字符串前后颠倒之后和该字符串一样的字符串。比如:a,aaaa,aba,abba…

最长回文子串

要求最长回文子串,就须要遍历每个子串,时间复杂度是O(N²);推断字串是不是回文,时间复杂度是O(N),这种话算法的时间复杂度就是O(N³).

我刚開始想到的就是中心扩展法,代码例如以下:

public static String getLongestPalindrome(String str) {        if(str.isEmpty() || str.length() == 1) {            return str;        }                String longest = str.substring(0, 1);          for (int i = 0; i < str.length(); i++) {              // get longest palindrome with center of i              String tmp = helper(str, i, i);              if (tmp.length() > longest.length()) {                  longest = tmp;              }                // get longest palindrome with center of i, i+1              tmp = helper(str, i, i + 1);              if (tmp.length() > longest.length()) {                  longest = tmp;              }          }          return longest;    }        private static String helper(String str, int begin, int end) {        while (begin >= 0 && end <= str.length() - 1                  && str.charAt(begin) == str.charAt(end)) {              begin--;              end++;          }          String result = str.substring(begin + 1, end);          return result;      }

中心扩展法的时间复杂度为O(N²).

写完之后一直在想,有没有更厉害的办法呢。能将时间复杂度杀到O(N)呢,一直想也想不到,后来上网搜到了传说中的Manacher算法。

我们先来看一下代码:

public static int[] getPalindromeLength(String str) {    	StringBuilder newStr = new StringBuilder();    	newStr.append("#");    	for(int i = 0; i < str.length(); i++) {    		newStr.append(str.charAt(i));    		newStr.append("#");    	}    	    	int[] rad = new int[newStr.length()];    	    	// the right edge of the longest sub palindrome string    	int right = -1;    	// the center of the longest sub palindrome string    	int id = -1;    	    	for (int i = 0; i < newStr.length(); i++) {    	    // define the minimum radius    		int r = 1;            if (i <= right) {                r = Math.min(right - i, rad[2 * id - i]);            }                        // try to get a lager radius            while (i - r >= 0 && i + r < newStr.length()             		&& newStr.charAt(i - r) == newStr.charAt(i + r)) {                r++;            }                        //update the right edge and the center of the longest sub palindrome string            if (i + r - 1> right) {                right = i + r - 1;                id = i;            }            rad[i] = r;    	}    	    	return rad;    }

首先。Manacher算法提供了一个巧妙解决长度为奇数与长度为偶数的不同回文办法。在每一个字符见插入一个原字符串未出现过的特殊字符,普通情况下用“#”。

这样无论是aba类型的回文还是abba类型的回文。插入特殊字符之后,#a#b#a#和#a#b#b#a#的长度肯定是奇数,这样就攻克了上面的问题。

Manacher算法引入一个辅助数组来记录以每一个字符为中心的最长回文串的信息,Rad[i]记录的是以字符str[i]为中心的最长回文串。当以str[i]为中心,这个最长回文串向两边延伸Rad[i]个字符。

原串:abbac

新串:#a#b#b#a#c#

辅助数组:12 1 2 5 2 1 2 1 2 1

那么Manacher算法是怎么计算辅助数组Rad的呢?

我们从左往右依次计算Rad[i],当计算Rad[i]时,Rad[j](0<=j<i)已经计算完成。

我们如果整型right为当前最长回文子串的最右边缘。而且设当前最长回文子串的中心点为id,那么当前指针的位置i就有两种情况:

第一种:i<=right

那么找到i相对于中心点的对称的位置j(2*id-i)。那么假设Rad[j]<right-i。例如以下图:

那么说明以j为中心的回文串一定在以id为中心的回文串的内部,且j和i关于位置id对称,由回文串的定义可知。一个回文串反过来还是一个回文串,所以以i为中心的回文串的长度至少和以j为中心的回文串一样,即Rad[i]>=Rad[j]。由于Rad[j]<right-i。所以说i+Rad[j]<right。由对称性可知Rad[i]=Rad[j]。

假设Rad[j]>=right-i,由对称性。说明以i为中心的回文串可能会延伸到right之外,而大于right的部分我们还没有进行匹配。所以要从right+1位置開始一个一个进行匹配,直到发生失配,从而更新right和相应的id以及Rad[i]。

另外一种情况:i>right

假设i比right还要大,说明对于中点为i的回文串还一点都没有匹配,这个时候,就仅仅能老老实实地一个一个匹配了。匹配完毕后要更新right的位置和相应的id以及Rad[i]。

总结

 

1.  Manacher算法先巧妙的在全部字符间插入特殊字符,非常好的攻克了回文字串偶数长度和奇数长度不同处理方法的问题。

2.  事实上Manacher算法的复杂度不仅仅O(N),可是显然是介于O(N)和O(N²)之间,是眼下时间复杂度最低的回文子串算法。

你可能感兴趣的文章
消息队列的面试题6
查看>>
最小割dp Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) E
查看>>
2018-2019-2 20175311 实验一《Java开发环境的熟悉》实验报告
查看>>
修改linux最大文件句柄数
查看>>
网络编程---tcp/udp协议
查看>>
jmeter3.2 版本完美实现Load Test报表
查看>>
再看python多线程------threading模块
查看>>
R 从零开始,简单API集合
查看>>
学习react系列(八)—— mixins迁移
查看>>
《工作DNA》摘录三
查看>>
Daily Scrum 12.9
查看>>
PHP之道推荐使用PHP版本,数据库方式,以及虚拟机的创建程序
查看>>
YAML文件的使用及参数服务器
查看>>
5.7-多源复制搭建
查看>>
now()与sysdate()的区别(1)
查看>>
jmeter压力测试值之配置JDBC Connection Configuration(一)
查看>>
linux每日命令(10):touch命令
查看>>
给natp_server加缓存
查看>>
Index Generation
查看>>
多系统通讯-DotNetMQ
查看>>