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

1. 回文串

作为程序员,回文串这个词已经见怪不怪了,就是一个字符串正着读和反着读是一样的,形式如abcdcba、bbaabb。这里涉及到奇回文和偶回文,奇回文指回文串的字符数是奇数,偶回文指回文串的字符数是偶数。前面举的abcdcba就是奇回文,bbaabb就是偶回文。判断一个字符串是否是回文串很简单,只要从字符串的两端开始往中间扫描,全部匹配成功则是回文串,只要有一次匹配失败,那么就不是回文串。代码如下

// 没有对字符串为null或者空串的返回值进行考虑static boolean Palindrome(String s){    for(int i = 0, j = s.length()-1; i < j; i  , j--){        if(s.charAt(i) != s.charAt(j)){            return false;        }    }    return true;}

2. 最长回文串

在我们了解回文串内容后,如果给你一个字符串,你能不能得到该字符串中的最长回文串呢?

2.1 暴力匹配法

最长回文串简单的解法就是暴力匹配法,依次判断所有字符数大于1个的子串是否回文串,并记录最长的那个回文串。如acbc字符串,得到字符数大于1的子串ac、cb、bc;acb、cbc;acbc,其中cbc是最长回文串。虽然暴力匹配法思路清晰、代码简单,但是如果字符串长度较长时,那么子串的数量是很庞大的,对于一个长度为n的字符串,它的子串有n(n-1)/2个,加上判断子串是否为回文串的时间复杂度是O(n),所以最终总的时间复杂度是O(n^3)左右。暴力匹配留给大家自行编写代码,博主就偷个懒不写了。

2.2 中心扩散法

中心扩散法是另一种回文串解决方法,算法思路是从字符串的第一个字符一直遍历到最后一个字符,每次从该字符往两边扫描,如果左右两边的值相等,那么往左右两边拓展,直至左右两边的值不相等或者越界,扫描结束,记录此时的左右边界下标,并且记录此时的回文串长度。该方法的时间消耗主要是遍历字符串的每个字符,以及每个字符需要向两边拓展扩散,所以总的时间复杂度为O(n^2)。

图解:以下以abcfcbd字符串遍历到 f 字符进行图解,如下图。

1. 当遍历abcfcbd字符串的 f 字符时,先令left和right都指向 f 字符。

2. 往左右拓展,可以拓展,left往左移,right往右移

3. 可以拓展,继续移动

4. 不可以继续拓展,结束,记录left和right的位置

代码

 public String longestPalindrome(String s) {     int len = s.length();     if(len <= 1){         return s;     }                  int max = 0;     int[] index = new int[2];     for(int i = 0; i < len-1; i  ){         // 考虑奇数回文还是偶数回文,所以分别计算以i为中心,以i和i 1为中心两种方式的回文串         int[] f1 = findSub(s, i, i);         int[] f2 = findSub(s, i, i 1);         int f1Len = f1[1] - f1[0];         int f2Len = f2[1] - f2[0];         // 如果以i为中心的奇回文串长度更长并且大于前面记录的最大回文串长度max,更新max         // 如果以i和i 1为中心的偶回文串长度更长并且大于前面记录的最大回文串长度max,更新max         if((f1Len > f2Len) && (f1Len > max)){             index[0] = f1[0];             index[1] = f1[1];             max = f1Len;         }else if((f1Len <= f2Len) && (f2Len > max)){             index[0] = f2[0];             index[1] = f2[1];             max = f2Len;         }     }     return s.substring(index[0], index[1] 1); } static int[] findSub(String s, int left, int right){     // 如果是偶数回文,left和right不等,需要判断一下left和right的值是否相等     if(s.charAt(left) != s.charAt(right)){         return new int[]{left 1, left 1};     }     while((left >= 0) && (right <= s.length()-1) && (s.charAt(left) == s.charAt(right))){         left--;         right  ;     }     return new int[]{left 1, right-1}; }

2.3 Manacher算法

Manacher算法是一种以O(n)时间复杂度得到最长回文串的算法,以该算法的发明者Manacher老先生名字命名。虽然该算法的解释网上较多,但是有点繁琐和难懂,博主尽量以自己小白的理解力详细地进行说明。我们接下来先说说Manacher算法的主要思想,它到底在哪里进行了优化?然后我们再上代码。接下来我们以dcbcdcbca字符串为例,请耐心阅读。

2.3.1. 对字符串dcbcdcbca先预处理。

在每个字符两旁插入分割符,可以是任意字符,因为博主一开始也觉得分隔符不能是字符串中出现的字符,那这里选取'a'字符作为分割符进行证明,预处理后得到如下字符串str2。

2.3.2. 记录每个字符的回文半径

遍历每个字符时,将每个字符可以向左右两边拓展的长度称为回文半径,使用val数组记录回文半径。则str2的第1个字符到第13个字符回文半径数据值如下图所示。

2.3.3 Manacher算法的优化之处

其实计算str2的第1个字符到第13个字符回文半径时Manacher也有优化,只是接下来更好讲解,所以现在分析。

当扫描到str2的第10个字符d时,此时的回文字符串是acabacadacabaca,如下图所示。

接下来我们要计算str2的第14个字符 b,正常情况下,我们以b为中心向两边拓展;Manacher算法的强大就是在此处进行了优化。

因为b处在axis和right之间,我们可以看看str2第14个b字符关于axis对称的第6个b字符它的回文半径是多少,为什么可以这样呢?

接下来看图解吧,原本以为自己理解了很好描述,但现在发现自己理解而已,要想描述清楚还是有点难,大家看看图解吧!

1. 步骤1

2. 步骤2

3. 步骤3

总结:Manacher算法进行优化的部分主要有两点:①字符串预处理,添加分割符;②利用回文串的对称信息,避免重复计算回文半径。

看来这种算法还是有些难描述的,大家见谅,还是只能多花点时间去消化,Manacher算法最重要一点就是利用对称信息。

代码

public String longestPalindrome(String s) {        int len = s.length();        int newLen = 2 * len   1;        // 字符串预处理,得到填充分隔符后的字符数组        char[] newStr = new char[newLen];        for(int i = 0; i < len; i  ){            newStr[2*i] = 'a';            newStr[2*i 1] = s.charAt(i);        }        newStr[newLen-1] = 'a';        // ans是最长回文串的回文半径,ansIndex是最长回文串的对称中心        int[] val = new int[newLen];        int axis = 0;        int right = 0;        int ans = 0;        int ansIndex = 0;        for(int i = 0; i < newLen; i  ){            // 如果当前遍历字符处于回文串的最远边界内,那么可以利用对称信息            if(i < right){                val[i] = Math.min(val[2*axis-i], right-i 1);            }else{                val[i] = 1;            }            // 没有越界,并且回文串向左右拓展成功,那么回文半径加1            while(i-val[i] >= 0 && i val[i] < newLen && newStr[i-val[i]] == newStr[i val[i]]){                val[i]  ;            }            // 如果当前遍历字符的边界大于记录的最远边界,更新回文串的最远边界            if(i val[i]-1 > right){                right = i val[i]-1;                axis = i;            }            // 记录最长回文串的回文半径和对称中心            if(val[i] > ans){                ans = val[i];                ansIndex = i;            }        }                StringBuilder sb = new StringBuilder();        for(int i = ansIndex-ans 1; i < ansIndex ans-1; i  ){            sb.append(newStr[  i]);        }        return sb.toString();    }

以下是力扣的运行结果

来源:https://www.icode9.com/content-1-789301.html

(0)

相关推荐

  • 【重要】数组与指针不等价

    【重要】数组与指针不等价

  • ​LeetCode刷题实战387:字符串中的第一个唯一字符

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  • (1条消息) 这题不会!面壁思过,换行吧!

    今天是小浩算法 "365刷题计划" 第107天.满血,复活! 即日起,我们从这个题目开始,把 leetcode 前 200 道题,还没有讲过的,全部讲一遍. 暂定的目标是一周 3- ...

  • Python|字符串中第二大的数字

    问题描述给你一个混合字符串s,请你返回s中第二大的数字,如果不存在第二大的数字,请你返回-1.混合字符串由小写英文字母和数字组成.示例:输入:s = 'dfa12321afd'输出:2解决方案这是一道 ...

  • ​LeetCode刷题实战409:最长回文串

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  • 【LeetCode Hot 100 最长回文子串】

    新年的刷的第一题,题目如下: 给你一个字符串 s,找到 s 中最长的回文子串. 示例 1: 输入:s = "babad" 输出:"bab" 解释:"a ...

  • Python刷题:最长回文子串(字符串)

    题目描述 给定一个仅包含小写字母的字符串,求它的最长回文子串的长度.所谓回文串,指左右对称的字符串. 解题思路 当字符串不为空时,回文子串最少也是一个字符,即初始长度为1,当回文子串更长时,就可能有两 ...

  • ​LeetCode刷题实战125:验证回文串

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  • 《菩萨蛮·回文夏闺怨》苏轼 | 郎笑藕丝长,长丝藕笑郎

    古诗词文欣赏 2020-06-13 菩萨蛮·回文夏闺怨 作者:苏轼  朗诵:妍婷姝 柳庭风静人眠昼,昼眠人静风庭柳. 香汗薄衫凉,凉衫薄汗香. 手红冰碗藕,藕碗冰红手. 郎笑藕丝长,长丝藕笑郎. 译文 ...

  • 〔思归客〕特邀作家“弓长”作品‖《荷柳五首》(回文诗)外一首

    华夏思归客诗词学会欢迎您 荷柳五首(回文诗) 文/弓长 一 (十字回文) 痴荷笑柳醉香池 柳醉香池映画诗 诗画映池香醉柳 池香醉柳笑荷痴 二 (通体回文) 春来池碧荷花磬 燕舞风柔柳翠林 新曲一舟轻唱 ...

  • 【夜听】《菩萨蛮·回文夏闺怨》宋代,苏轼

    菩萨蛮·回文夏闺怨 [宋代]苏轼 柳庭风静人眠昼,昼眠人静风庭柳. 香汗薄衫凉,凉衫薄汗香. 手红冰碗藕,藕碗冰红手. 郎笑藕丝长,长丝藕笑郎. ▲ 点上方绿标即可收听主播朗读诗词 译文 院无风,柳丝 ...

  • 栗江风韵之惜缘回文专辑

    桃林赏花 文/惜缘 香分几瓣小红花,尽占春光艳抹霞. 长忆醉游神在乐,久思相遇梦来赊. 茫茫草动风声乱,隐隐枝摇树影斜. 阳暖沐身闲得意,藏人美景画中嗟. 回文 嗟中画景美人藏,意得闲身沐暖阳. 斜影 ...

  • 栗江风韵 | 惜缘回文诗专辑(2)

    作者简介 崔丰萍,网名:惜缘,江西省上栗县人,业余诗歌爱好者. 四季回文诗 春回绿 春回绿点万山青,翠叠层峦峰送迎. 新色柔光晴日丽,嫩颜娇蕊玉香清. 人迷景里风摇树,蝶引花间柳落莺. 真成梦境奇逢巧 ...