最大回文字符串算法详解与优化

背景

最近开始研究算法,于是在leetcode上做算法题,第五题Longest Palindromic Substring便是关于回文子串的。

什么是回文字串

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

最长回文子串

要求最长回文子串,就需要遍历每一个子串,时间复杂度是O(N2);判断字串是不是回文,时间复杂度是O(N),这样的话算法的时间复杂度就是O(N3).

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

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(N2).

写完之后一直在想,有没有更厉害的办法呢,能将时间复杂度杀到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(N2)之间,是目前时间复杂度最低的回文子串算法。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-23 15:26:21

最大回文字符串算法详解与优化的相关文章

基础排序算法详解与优化

常见的基础排序有选择排序.冒泡排序和插入排序.众所周知,他们的时间复杂度是 O(n\*n). 但是,现在要重新认识一下基础排序算法,尤其是“插入排序”:在近乎有序的情况下,插入排序的时间复杂度可以降低到 O(n)的程度. 因此,在处理系 原文地址:https://www.cnblogs.com/wdyff/p/9749837.html

【LeetCode-面试算法经典-Java实现】【05-Longest Palindromic Substring(最大回文字符串)】

背景 近期開始研究算法,于是在leetcode上做算法题,第五题Longest Palindromic Substring便是关于回文子串的. 什么是回文字串 回文字符串是指将该字符串前后颠倒之后和该字符串一样的字符串.比如:a,aaaa,aba,abba- 最长回文子串 要求最长回文子串,就须要遍历每个子串,时间复杂度是O(N2):推断字串是不是回文,时间复杂度是O(N),这种话算法的时间复杂度就是O(N3). 我刚開始想到的就是中心扩展法,代码例如以下: public static Stri

最长回文子串算法(字符串处理问题+多种方法解决)【转载】

转载地址:http://blog.csdn.net/kangroger/article/details/37742639 回文是指正着读和倒着读,结果一些样,比如abcba或abba. 题目是要在一个字符串中要到最长的回文子串. 1.暴力法 最容易想到的就是暴力破解,求出每一个子串,之后判断是不是回文,找到最长的那个. 求每一个子串时间复杂度O(N^2),判断子串是不是回文O(N),两者是相乘关系,所以时间复杂度为O(N^3). string findLongestPalindrome(stri

查找字符串中的最长回文字符串---Manacher算法

转载:https://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号.比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#. 为了进一步减少编码的复杂度,可以在字符串的开始和结尾加入另一个特殊字符这样就不用特殊处理越界问题,比如%#a#b#a#@;(如果是C++,字符串末尾有一个\0,故结尾处不需要添加额为的特殊字符@) 然后用一个数组

hdu3068 求一个字符串中最长回文字符串的长度 Manacher算法

最长回文 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 31611    Accepted Submission(s): 11618 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.回文就是正反读都是一样的字符串,如aba, abba等 Input 输入有多组

Manacher算法详解

[转] Manacher算法详解 转载自: http://blog.csdn.net/dyx404514/article/details/42061017 Manacher算法 算法总结第三弹 manacher算法,前面讲了两个字符串相算法——kmp和拓展kmp,这次来还是来总结一个字符串算法,manacher算法,我习惯叫他 “马拉车”算法. 相对于前面介绍的两个算法,Manacher算法的应用范围要狭窄得多,但是它的思想和拓展kmp算法有很多共通支出,所以在这里介绍一下.Manacher算法

KMP算法详解(图示+代码)

算法过程非常绕,不要企图一次就能看明白,多尝试就会明白一些.下面试图用比较直观的方法解释这个算法,对KMP算法的解释如下: 1. 首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较.因为B与A不匹配,所以搜索词后移一位. 2. 因为B与A不匹配,搜索词再往后移. 3. 就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止. 4. 接着比较字符串和搜索词的下一个字符,还是相同. 5. 直到字

【转】AC算法详解

原文转自:http://blog.csdn.net/joylnwang/article/details/6793192 AC算法是Alfred V.Aho(<编译原理>(龙书)的作者),和Margaret J.Corasick于1974年提出(与KMP算法同年)的一个经典的多模式匹配算法,可以保证对于给定的长度为n的文本,和模式集合P{p1,p2,...pm},在O(n)时间复杂度内,找到文本中的所有目标模式,而与模式集合的规模m无关.正如KMP算法在单模式匹配方面的突出贡献一样,AC算法对于

机器学习经典算法详解及Python实现---朴素贝叶斯分类及其在文本分类、垃圾邮件检测中的应用

摘要: 朴素贝叶斯分类是贝叶斯分类器的一种,贝叶斯分类算法是统计学的一种分类方法,利用概率统计知识进行分类,其分类原理就是利用贝叶斯公式根据某对象的先验概率计算出其后验概率(即该对象属于某一类的概率),然后选择具有最大后验概率的类作为该对象所属的类.总的来说:当样本特征个数较多或者特征之间相关性较大时,朴素贝叶斯分类效率比不上决策树模型:当各特征相关性较小时,朴素贝叶斯分类性能最为良好.另外朴素贝叶斯的计算过程类条件概率等计算彼此是独立的,因此特别适于分布式计算.本文详述了朴素贝叶斯分类的统计学