最长回文子串(Longest Palindromic Substring)——三种时间复杂度的解法「建议收藏」

最长回文子串(Longest Palindromic Substring)——三种时间复杂度的解法「建议收藏」  子串:小于等于原字符串长度由原字符串中任意个连续字符组成的子序列  回文:关于中间字符对称的文法,即“aba”(单核)、“cabbac”(双核)等  最长回文子串:1.寻找回文子串;2.该子串是回文子串中长度最长的。一、O(n^3)时间复杂度方法——暴力求解1.思想:    1)从最长的子串开始,遍历所有该原字符串的子串;    2)每找出一个字符串,就判断该字符串是否为回文;  …

大家好,又见面了,我是你们的朋友全栈君。

    子串:小于等于原字符串长度由原字符串中任意个连续字符组成的子序列

    回文:关于中间字符对称的文法,即“aba”(单核)、“cabbac”(双核)等

    最长回文子串:1.寻找回文子串;2.该子串是回文子串中长度最长的。

一、O(n^3)时间复杂度方法——暴力求解

1.思想:

    1)从最长的子串开始,遍历所有该原字符串的子串;

    2)每找出一个字符串,就判断该字符串是否为回文;

    3)子串为回文时,则找到了最长的回文子串,因此结束;反之,则继续遍历。


2.时间复杂度解释:

    遍历字符串子串:嵌套一个循环、O(n^2);   

    判断是否为回文:再次嵌套一个循环、O(n^3)。


3.java代码详解:

    public static String longestPalindrome(String s) {

        if(s.length() <= 1)
            return s;
        for(int i = s.length();i > 0; i–) {//子串长度
            for (int j = 0; j <= s.length() – i; j++) {

                String sub = s.substring(j , i + j);//子串位置
                int count = 0;//计数,用来判断是否对称
                for (int k = 0; k < sub.length() / 2; k++) {//左右对称判断
                    if (sub.charAt(k) == sub.charAt(sub.length() – k – 1))
                        count++;
                }
                if (count == sub.length() / 2)
                    return sub;
            }
        }
        return “”;//表示字符串中无回文子串

    }


二、O(n^2)时间复杂度方法——从中心向外扩散

1.思想:

     1)将子串分为单核和双核的情况,单核即指子串长度为奇数,双核则为偶数;

    2)遍历每个除最后一个位置的字符index(字符位置),单核:初始low = 初始high = index,low和high均不超过原字符串的下限和上限;判断low和high处的字符是否相等,相等则low++、high++(双核:初始high = 初始low+1 = index + 1);

    3)每次low与high处的字符相等时,都将当前最长的回文子串长度与high-low+1比较。后者大时,将最长的回文子串改为low与high之间的;

    4)重复执行2)、3),直至high-low+1 等于原字符串长度或者遍历到最后一个字符,取当前截取到的回文子串,该子串即为最长的回文子串。


2.时间复杂度解释:

   遍历字符:一层循环、O(n-1);

   找以当前字符为中心的最长回文子串:嵌套两个独立循环、O(2n*(n-1)) = O(n^2)。


3.java代码详解:

private static int maxLen = 0;

private static String sub = “”;

public static String longestPalindrome(String s) {

        if(s.length() <= 1)
            return s;

        for(int i = 0;i < s.length()-1;i++){

            findLongestPalindrome(s,i,i);//单核回文

            findLongestPalindrome(s,i,i+1);//双核回文
        }
        return sub;
    }
    public static  void findLongestPalindrome(String s,int low,int high){

        while (low >= 0 && high <= s.length()-1){

            if(s.charAt(low) == s.charAt(high)){

                if(high – low + 1 > maxLen){

                    maxLen = high – low + 1;
                    sub = s.substring(low , high+1);
                }
                low –;//向两边扩散找当前字符为中心的最大回文子串
                high ++;
            }
            else
                break;
        }

    }


三、O(n)时间复杂度方法——Manacher算法

1.思想:

    1)将原字符串S的每个字符间都插入一个永远不会在S中出现的字符(本例中用“#”表示),在S的首尾也插入该字符,使得到的新字符串S_new长度为2*S.length()+1,保证Len的长度为奇数(下例中空格不表示字符,仅美观作用);

        例:S:       a  a  b  a  b  b  a

        S_new:        #  a  #  a  #  b  #  a  #  b  #  b  #  a  #

    2)根据S_new求出以每个字符为中心的最长回文子串的最右端字符距离该字符的距离,存入Len数组中,即S_new[i]—S_new[r]为S_new[i]的最长回文子串的右段(S_new[2i-r]—S_new[r]为以S_new[i]为中心的最长回文子串),Len[i] = r – i + 1;

        S_new:        #  a  #  a  #  b  #  a  #  b  #  b  #  a  #

        Len:            1  2  3  2  1   4  1  4  1  2  5   2  1  2  1

        Len数组性质:Len[i] – 1即为以Len[i]为中心的最长回文子串在S中的长度。在S_new中,以S_new[i]为中心的最长回文子串长度为2Len[i] – 1,由于在S_new中是在每个字符两侧都有新字符“#”,观察可知“#”的数量一定是比原字符多1的,即有Len[i]个,因此真实的回文子串长度为Len[i] – 1,最长回文子串长度为Math.max(Len) – 1。

    3)Len数组求解(线性复杂度(O(n))):

       a.遍历S_new数组,i为当前遍历到的位置,即求解以S_new[i]为中心的最长回文子串的Len[i];

       b.设置两个参数:sub_midd = Len.indexOf(Math.max(Len)表示在i之前所得到的Len数组中的最大值所在位置、sub_side = sub_midd + Len[sub_midd] – 1表示以sub_midd为中心的最长回文子串的最右端在S_new中的位置。起始sub_midd和sub_side设为0,从S_new中的第一个字母开始计算,每次计算后都需要更新sub_midd和sub_side;

      c.当i < sub_side时,取i关于sub_midd的对称点j(j = 2sub_midd – i,由于i <= sub_side,因此2sub_midd – sub_side <= j <= sub_midd);当Len[j] < sub_side – i时,即以S_new[j]为中心的最长回文子串是在以S_new[sub_midd]为中心的最长回文子串的内部,再由于i、j关于sub_midd对称,可知Len[i] = Len[j];

最长回文子串(Longest Palindromic Substring)——三种时间复杂度的解法「建议收藏」

    当Len[j] >= sub.side – i时说明以S_new[i]为中心的回文串可能延伸到sub_side之外,而大于sub_side的部分还没有进行匹配,所以要从sub_side+1位置开始进行匹配,直到匹配失败以后,从而更新sub_side和对应的sub_midd以及Len[i];

最长回文子串(Longest Palindromic Substring)——三种时间复杂度的解法「建议收藏」

        d.当i > sub_side时,则说明以S_new[i]为中心的最长回文子串还没开始匹配寻找,因此需要一个一个进行匹配寻找,结束后更新sub_side和对应的sub_midd以及Len[i]。

最长回文子串(Longest Palindromic Substring)——三种时间复杂度的解法「建议收藏」


2.时间复杂度解释:

        算法只有遇到还没匹配的位置时才进行匹配,已经匹配过的位置不再进行匹配,因此大大的减少了重复匹配的步骤,对于S_new中的每个字符只进行一次匹配。所以该算法的时间复杂度为O(2n+1)—>O(n)(n为原字符串的长度),所以其时间复杂度依旧是线性的。


3.java代码详解:

public String longestPalindrome(String s) {

        List<Character> s_new = new ArrayList<>();
        for(int i = 0;i < s.length();i++){

            s_new.add(‘#’);
            s_new.add(s.charAt(i));
        }
        s_new.add(‘#’);
        List<Integer> Len = new ArrayList<>();
        String sub = “”;//最长回文子串
        int sub_midd = 0;//表示在i之前所得到的Len数组中的最大值所在位置
        int sub_side = 0;//表示以sub_midd为中心的最长回文子串的最右端在S_new中的位置
        Len.add(1);
        for(int i = 1;i < s_new.size();i++){

            if(i < sub_side) {//i < sub_side时,在Len[j]和sub_side – i中取最小值,省去了j的判断
                int j = 2 * sub_midd – i;
                if(j >= 2 * sub_midd – sub_side &&  Len.get(j) <= sub_side – i){

                    Len.add(Len.get(j));
                }
                else
                    Len.add(sub_side – i + 1);
            }
            else//i >= sub_side时,从头开始匹配
                Len.add(1);
            while( (i – Len.get(i) >= 0 && i + Len.get(i) < s_new.size()) && (s_new.get(i – Len.get(i)) == s_new.get(i + Len.get(i))))
                Len.set(i,Len.get(i) + 1);//s_new[i]两端开始扩展匹配,直到匹配失败时停止
            if(Len.get(i) >= Len.get(sub_midd)){//匹配的新回文子串长度大于原有的长度
                sub_side = Len.get(i) + i – 1;
                sub_midd = i;
            }
        }
        sub = s.substring((2*sub_midd – sub_side)/2,sub_side /2);//在s中找到最长回文子串的位置
        return sub;

    }

写这篇博客也是为了加深印象,希望可以通过这种方式来提高学习效率。共勉!





    

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/130512.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux drupal 7安装教程,Linux上Drupal7安装教程

    linux drupal 7安装教程,Linux上Drupal7安装教程前言:国内用drupal的并不太多,网上资料也很少。要注意的是drupal尽量别使用apt来安装,特别是UbuntuLinux平台的drupal做出了一定的更改,会妨碍后期的学习和使用。在安装drupal前要先完成LAMP的搭建,如果没有安装可以参照我之前的文章http://www.linuxidc.com/Linux/2016-03/128983.htm或者使用tasksel安装,这里不再…

    2022年5月20日
  • Vue生成二维码_生成二维码插件

    Vue生成二维码_生成二维码插件首先安装一下面这个插件cnpminstallvue-qr-S接下来在需要生产并下载二维码的页面引入importVueQrfrom’vue-qr’并在components中注册为组件components:{VueQr//二维码组件},data(){ return{ config:{//二维码配置logo:”,value:”} }

    2022年9月25日
  • c语言的fprintf函数的用法,fprintf 和fscanf 函数

    c语言的fprintf函数的用法,fprintf 和fscanf 函数C语言的fprintf和fscanf函数fprintf()函数用于将一组字符写入文件。它将格式化的输出发送到流。fprintf()函数的语法如下:intfprintf(FILE*stream,constchar*format[,argument,…])示例:创建一个源文件:fprintf-write-file.c,其代码如下-#includemain(){FILE…

    2022年10月18日
  • postman中文版界面_英雄连2菜单汉化界面

    postman中文版界面_英雄连2菜单汉化界面参考:大佬的Gitee地址:gitee地址

    2022年9月29日
  • plsqldev连接远程oracle[通俗易懂]

    plsqldev连接远程oracle[通俗易懂]本地电脑安装了oracle客户端的情况Tns配置方法:1.找到oracle安装目录下的tnsnames.ora文件F:\app\Administrator\product\11.2.0\dbhome_1\NETWORK\ADMIN找到tnsnames.ora文件,用记事本打开tnsnames.ora文件添加本地实例名=(DESCRIPTION=(ADDRESS

    2022年5月29日
  • Linux抓包命令tcpdump以及常见抓包使用方法[通俗易懂]

    Linux抓包命令tcpdump以及常见抓包使用方法[通俗易懂]系统运行过程中,难免发现服务器一些流量异常或访问异常,我们可以采用tcpdump命令进行抓包。下边简单介绍下该命令使用办法。1,安装tcpdump一些系统默认没有安装,我们需要yum安装下这个命令:yuminstalltcpdump-y2,tcpdump使用。一些使用我们可以tcpdump–help看下帮助,比如:常用的一些简单用法,一般是抓取对应网卡、端口、对应IP等。tcpdump-nn本地网卡地址以及外部链接地址,并采用IP地址、端口表示。如果不加-nn则会…

    2022年6月15日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号