leetcode打卡12:题号5:最长回文子串

题目:

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:

输入: “cbbd”
输出: “bb”

解法一(暴力解法):

暴力解法其实很简单,就是不停的去抽取子串,然后判断是否为回文串即可.
但是这里我们的方法在提交的时候会显示超出时间限制,因为这个时间复杂度是O(n3)

public String longestPalindrome(String s) {
    String resultStr = "";
    int result = 1;
    for(int i = 0; i < s.length(); i++){
        for(int j = s.length();j > i; j--){
            int resultLen = j - i + 1;
            String str = s.substring(i,j);
            if(isValite(str) && resultLen > result ){
                result = Math.max(result,j-i+1);
                resultStr = str;
                break;
            }
        }
    }
    return resultStr;
}

public Boolean isValite(String str){
    for(int i = 0, j = str.length()-1; i != j && i < j;i++,j--){
        if(str.charAt(i) != str.charAt(j)) return false;
    }
    return true;
}

解法二(最长公共子串):

常见错误:

有些人会忍不住提出一个快速的解决方案,不幸的是,这个解决方案有缺陷(但是可以很容易地纠正):

反转 S,使之变成 S’ ,找到 S 和 S’之间最长的公共子串,这也必然是最长的回文子串。
这似乎是可行的,让我们看看下面的一些例子。

例如,S = “caba”, S’ =“abac”
S 以及 S’之间的最长公共子串为“aba”,恰恰是答案。

让我们尝试一下这个例子:S = “abacdfgdcaba”, S’=“abacdgfdcaba”:
S 以及 S’之间的最长公共子串为“abacd”。显然,这不是回文。

算法设计:

1.我们可以看到,当 SS 的其他部分中存在非回文子串的反向副本时,最长公共子串法就会失败。为了纠正这一点,每当我们找到最长的公共子串的候选项时,都需要检查子串的索引是否与反向子串的原始索引相同。如果相同,那么我们尝试更新目前为止找到的最长回文子串;如果不是,我们就跳过这个候选项并继续寻找下一个候选。

2.对于最长公共子串,我这里专门写了一篇博客讲解动态规划实现最长公共子序列和最长公共子串

https://blog.csdn.net/weixin_44844089/article/details/105372089

解法三(中心扩散):

中心思想:
1.例如在“xaay”中,我们找到了一个回文串为“aa”的时候,我们只需要判断 x ==y 就可以判断“xaay”是否为一个回文串

2.例如我们在“xabay”,我们找到了“aba”为回文串的之后,还是要判断x == y,然后就可以进行判断了

3.所以我们这里的核心思想就算是寻找到一位的回文,两位的回文,三位的回文,然后去找更长的回文

算法设计:
1.如何理解中心扩散,其实就是每次定位一个坐标,然后从这个字符开始向两边同时扩散寻找最长的回文串,扩散分为三步

2.例如:s = “abcccbs” (假设已经定位到了s【3】 = c)

3.此时i = 3,left = i - 1,right = i + 1 ,left代表当前定位字符左边的字符,right代表当前定位字符右边的字符

4.一路向左遍历,此时s【left】 = s【i】 = c,那就意味着s【left…i】此时就是一个回文串,之后left - 1,继续看左边是否相等,直到不等位置,在这次的遍历结束后,我们会找到s【left+1…i】,这个子串是一个回文串,并且这个回文串一定是所有字符都相同的(本次遍历结束后找出的是cc)。此时left = 2 ,s【left】= b

5.再一路向右遍历,和上面一样,此时s【right】 = s【i】 = c,那就意味着此时s【i…right】是一个回文串,之后right+ 1,继续看右边是否相等,直到不等位置,本次结束后我们就可以找到 s【left + 1…right - 1】是一个回文串,并且所有字符相等(本次遍历结束后找出的是ccc)。right = 6 ,s【right】 = b

6.之后我们同时向左右遍历,因为此时s【left+1,right-1】已经是一个全部相同的回文了,我们此时只需要判断s【left】== s【right】是否相等,此时由于s【left】== s【right】 = b,那就说明此时s【left…right】就是一个回文串,然后将left - 1, right + 1,这样最后遍历结束后我们就可以找到 s【left+1…right- 1】是一个以 s【i】为中心的最长回文串。同时记录下标即可。

/**
 * 中心扩散 没有优化
 * @param s
 * @return
 */
public static String longestPalindrome2(String s) {
    if(s == null || s.equals("")) return "";
    int beginIndex = 0;
    int quitIndex = 0;
    int maxResult = 0;

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

        int left = i - 1;
        int right = i + 1;
        int len = 1;
        //往左边遍历
        while(left >= 0 && (s.charAt(left) == s.charAt(i))){
            left -= 1;
            len +=1;
        }
        //往右边遍历
        while(right <s.length() && (s.charAt(right) == s.charAt(i))){
            right += 1;
            len += 1;
        }
        //同时向左向右遍历
        while(left >= 0  && right < s.length() && (s.charAt(right) == s.charAt(left))){
            left -= 1;
            right += 1;
            len += 2;
        }

        if(len > maxResult){
            beginIndex = left + 1;
            quitIndex = right - 1;
            maxResult = len;
        }

    }

    return s.substring(beginIndex,quitIndex + 1);


}

解法四(中心扩散+动态规划)

理解:
1.动态规划其实大概的意思就是我们要保留之前小问题的计算结果,当我们计算大问题的时候会用到我们小问题的计算结果

2.这个算法的设计思想和解法三是一样的,只不过我们会保留之前的计算结果,举个例子,例如我现在要判断s【2…8】是否为回文,但是由于之前我已经判断过s【3…7】是不是回文,所以我将先判断s【3…7】是不是回文(这里只需要取出之前的判断结果即可,不需要重新计算),如果s【3…7】是回文,那么我就判断s【2】 == s【8】,这样就可以很简单的判断s【2…8】是否为回文

算法设计:
1.动态规划基本都是这样,先定义一个dp矩阵用来存储计算结果,所以我也定义dp【s.length】【s.length】 = 0;

2.dp【i】【j】 = 1 代表的含义是s【i…j】是回文,等于0则代表不是回文

3.那么我们每次判断s【i…j】是不是回文,就得看s【i+1…j-1】是不是回文,也就是看dp【i+1】【j-1】 == 1,

4.若dp【i+1】【j-1】 = 0, 那么dp【i】【j】 = 0,

5.若dp【i+1】【j-1】 = 1 并且 s【i】= s【j】,那么dp【i】【j】 = 1

6.所以我们无非就是遍历一个dp矩阵,分别进行判断即可。但是这里有几个问题需要注意:

  • 数组越界问题,由于有i+1,j-1,所以需要进行数组越界的判断
  • 矩阵遍历顺序问题,不能按照一般的先行后列的方式遍历,这也是我犯的一个错误,例如我们现在遍历到了dp【0】【3】,需要看dp【1】【2】的值,但是这时候我们还没有遍历到dp【1】【2】,所以我们就要注意,本次遍历要先列后行的方式
public static String longestPalindrome5(String s) {
    if(s.equals("")) return "";
    int[][] dp= new int[s.length()][s.length()];
    int beginIndex = 0;
    int quitIndex = 0;
    int lenResult = 1;

    for(int j = 0; j < s.length(); j++){ //列
        for(int i = 0; i <= j; i++){//行
            if(i == j){ //如果是一个字符 那就一定是回文
                dp[i][j] = 1;
            }else if(i + 1 > j - 1 && s.charAt(i) == s.charAt(j)){ //如果i + 1 > j - 1,那么此时d【i+1】【j-1】是不存在的
                dp[i][j] = 1;

            }else if(i + 1 <= j - 1 && dp[i+1][j-1]==1 && s.charAt(i) == s.charAt(j)){
                dp[i][j] = 1;
            }else {
                dp[i][j] = 0;
            }
            if(dp[i][j] == 1 && j-i+1 > lenResult){
                beginIndex = i;
                quitIndex = j;
                lenResult = j - i + 1;
            }
        }
    }

    return s.substring(beginIndex,quitIndex+1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,有三种方法可以解决LeetCode上的最长回文子串问题。 方法一是使用扩展中心法优化,即从左向右遍历字符串,找到连续相同字符组成的子串作为扩展中心,然后从该中心向左右扩展,找到最长的回文子串。这个方法的时间复杂度为O(n²)。\[1\] 方法二是直接循环字符串,判断子串是否是回文子串,然后得到最长回文子串。这个方法的时间复杂度为O(n³),效率较低。\[2\] 方法三是双层for循环遍历所有子串可能,然后再对比是否反向和正向是一样的。这个方法的时间复杂度也为O(n³),效率较低。\[3\] 综上所述,方法一是解决LeetCode最长回文子串问题的最优解法。 #### 引用[.reference_title] - *1* [LeetCode_5_最长回文子串](https://blog.csdn.net/qq_38975553/article/details/109222153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Leetcode-最长回文子串](https://blog.csdn.net/duffon_ze/article/details/86691293)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [LeetCode 第5题:最长回文子串(Python3解法)](https://blog.csdn.net/weixin_43490422/article/details/126479629)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值