最长回文子串 (动态规划法、中心扩展算法)

问题描述:

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

 

思考:

嗯,回文嘛!就是顺序读取和逆序读取的结果是一样的,那我们可以使用两个for循环来不断的截取给定的字符串S,然后判断截取后的字符串是不是回文串,与此同时,使用一个新的字符串result来保存我们截取到的并且长度最长的回文串。

 

代码:

​
public String longestPalindrome_reconstructure1(String s){ // 超出时间限制
        if (s.length() < 2){
            return s;
        }
        String result = s.substring(0,1);
        String temp = null;
        String temp1 = null;
        for(int i = 0; i < s.length() - 1; i++){
            temp = null;
            temp1 = null;
            for (int j = s.length(); j > i; j--){
                if (j - i < result.length()) break;
                temp = s.substring(i, j);
                temp1 = new StringBuilder(temp).reverse().toString(); // 对截取后的字符串进行反转

                if (temp.equals(temp1) && temp.length() > result.length()){
                    result = temp;
                }
            }
        }
        return result;
    }

​

上述代码倒是完成了得到最长回文子串的功能,但是,有个很明显的缺点就是时间开销太大了,所以肯定得想想办法减小时间开销

 

使用中心扩展算法:

我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,并且只有 2n - 1个这样的中心。

你可能会问,为什么会是 2n - 1 个,而不是 n个中心?原因在于所含字母数为偶数的回文的中心可以处于两字母之间(例如 :“abba” 的中心在两个b之间),时间复杂度也只有O(n​2​​)

 

代码:

​
    // 使用中心扩展算法
    public String longestPalindrome_reconstructure2(String s) { // 第二次对代码进行重构
        if (s.length() < 2) { // 单个字符肯定是回文串,直接返回s
            return s;
        }
        int maxLength = 0;
        int center = 0;
        for (int i = 0; i < s.length(); i++){
            int begin = centerExpand(s, i, i);          // 最长回文串长度为奇数
            int end = centerExpand(s, i, i + 1);   // 最长回文串长度为偶数

            if (maxLength < Math.max(begin, end)){
                center = i;                                // 以center为中心
                maxLength = Math.max(begin, end);          // 最长回文串长度
            }
        }
        // 如果我们的回文串的长度为偶数,那么中心左边的长度会比右边的长度小1
        return s.substring(center - (maxLength - 1) / 2, center + maxLength / 2 + 1);
    }

    int centerExpand(String s, int begin, int end){
        int left = begin, right = end;
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){
            left--;
            right++;
        }
        // 返回以begin,end为基准,同时向左向右扩展后能够得到的最长回文串长度
        return right - left - 1;
    }

​

使用动态规划法:

避免在验证回文时进行不必要的重复计算。考虑 “ababa” 这个示例。如果我们已经知道 “bab” 是回文,那么很明显,“ababa” 一定是回文,因为它的左首字母和右尾字母是相同的。

P(i,j)=(P(i+1,j−1) and S​i​​==S​j​​)

我们首先初始化一字母和二字母的回文,然后找到所有三字母回文,并依此类推…

R%`HVT[3D88A86WRHC5CM5Q

代码:

​
    // 使用动态规划法
    public String longestPalindrome_reconstructure3(String s) { // 第三次对代码进行重构
        if (s.length() < 2) { // 单个字符肯定是回文串,直接返回s
            return s;
        }
        boolean[][] dp = new boolean[s.length()][s.length()];  // 初始化一个二维数组,值默认是false
        String result = s.substring(0,1);
        for (int j = 0; j < s.length(); j++){
            for (int i = 0; i <= j; i++){
                dp[i][j] = s.charAt(i) == s.charAt(j) &&(j - i <= 2 || dp[i+1][j-1]);
                if (dp[i][j]){
                    if (j - i + 1 > result.length()){
                        result = s.substring(i, j + 1);
                    }
                }
            }
        }
        return result;
    }

​

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
题目描述: 给定一个字符串 s,找到 s 中最长的回文子串。 回文串是指正着读和反着读都一样的字符串。 示例: 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 输入: "cbbd" 输出: "bb" 解题思路: 1.暴力 暴力是最简单直接的方,遍历所有子串,判断是否为回文串,记录最长的回文串。 时间复杂度:O(n^3) 2.中心扩展 中心扩展是基于回文串的对称性质,从中心向两边扩展,判断是否为回文串,记录最长的回文串。 时间复杂度:O(n^2) 3.动态规划 动态规划是基于回文串的子串也是回文串的性质,利用状态转移方程,记录最长的回文串。 时间复杂度:O(n^2) 4.马拉车算法 马拉车算法是基于回文串的对称性质和回文串的最大半径,利用中心扩展的思想,减少重复计算,记录最长的回文串。 时间复杂度:O(n) 参考代码: 1.暴力 class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) return s; string res = ""; for (int i = ; i < n; i++) { for (int j = i; j < n; j++) { int len = j - i + 1; if (len > res.size() && isPalindrome(s, i, j)) { res = s.substr(i, len); } } } return res; } bool isPalindrome(string s, int left, int right) { while (left < right) { if (s[left] != s[right]) return false; left++; right--; } return true; } }; 2.中心扩展 class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) return s; string res = ""; for (int i = ; i < n; i++) { string s1 = palindrome(s, i, i); string s2 = palindrome(s, i, i + 1); res = res.size() > s1.size() ? res : s1; res = res.size() > s2.size() ? res : s2; } return res; } string palindrome(string s, int left, int right) { while (left >= && right < s.size() && s[left] == s[right]) { left--; right++; } return s.substr(left + 1, right - left - 1); } }; 3.动态规划 class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) return s; vector<vector<bool>> dp(n, vector<bool>(n, false)); int start = , maxLen = 1; for (int i = ; i < n; i++) { dp[i][i] = true; } for (int j = 1; j < n; j++) { for (int i = ; i < j; i++) { if (s[i] != s[j]) { dp[i][j] = false; } else { if (j - i < 3) { dp[i][j] = true; } else { dp[i][j] = dp[i + 1][j - 1]; } } if (dp[i][j] && j - i + 1 > maxLen) { maxLen = j - i + 1; start = i; } } } return s.substr(start, maxLen); } }; 4.马拉车算法 class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) return s; string t = "#"; for (int i = ; i < n; i++) { t += s[i]; t += "#"; } n = t.size(); vector<int> p(n, ); int center = , maxRight = , start = , maxLen = 1; for (int i = ; i < n; i++) { if (i < maxRight) { int mirror = 2 * center - i; p[i] = min(maxRight - i, p[mirror]); } int left = i - (p[i] + 1), right = i + (p[i] + 1); while (left >= && right < n && t[left] == t[right]) { p[i]++; left--; right++; } if (i + p[i] > maxRight) { center = i; maxRight = i + p[i]; } if (p[i] > maxLen) { maxLen = p[i]; start = (i - maxLen) / 2; } } return s.substr(start, maxLen); } };

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值