NC+LC:回文子串与最长回文子串

LC 647.回文子串

题目描述:
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 :
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

解法一:中心扩散法

思路

  • 遍历字符串,确定一个中心位置
  • 回文长度为奇数时,左右指针指向同一个位置,为偶数以左右指针所指的两个元素为中心
  • 找到中心后,往两边扩散,求回文子串的数量,当前位置的奇偶两种情况都需要考虑

实现代码

class Solution {
    int count = 0;
    public int countSubstrings(String s) {
        if(s.length() == 0 || s == null){
            return 0;
        }
        for (int i = 0; i < s.length(); i++) {
            extendPalindrome(s, i, i); //回文的长度为奇数,left与right指向的中心为同一个位置
            extendPalindrome(s, i, i + 1); //为偶数时,right指向left后一位
        }
        return count;
    }
    //以left和right为中心点,往两边扩散,求回文子串的数量
    public void extendPalindrome(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left--) == s.charAt(right++)) {
            count++;
        }
    }
}

优化

将求回文子串的方法放入循环中,注意中心点的个数为2*字符串长度-1,还有right的两种情况
class Solution {
    public int countSubstrings(String s) {
        if(s.length() == 0 || s == null){
            return 0;
        }
        //字符串的长度
        int length = s.length();
        //中心点的个数,如aba->a,ab,b,ba,a有5个中心点
        int size = 2 * length - 1;
        //回文串的个数
        int count = 0;
        for (int i = 0; i < size; ++i) {
            //right要么等于left,要么等于left+1
            //如果left等于right,那么中心点就是一个字符,如果left+1等于right,中心点就是2个字符
            int left = i / 2;
            int right = left + i % 2;
            while (left >= 0 && right < length && s.charAt(left--) == s.charAt(right++))
                ++count;
        }
        return count;
    }
}

复杂度分析

  • 时间复杂度:
  • 空间复杂度:

解法二:动态规划

思路

  • 定义二维数组dp,dp[l][r] 为 true,则表示字符串从 l 到 r 是回文子串
  • r表示子串右边界,l表示子串左边界,当 s.charAt(l) != s.charAt(r) 则从 l 到 r 不可能构成回文子串
  • 当 s.charAt(l) == s.charAt(r) 时,若该子串只有一个或两个字符,则为一个回文子串,如a,aa
  • 当有三个及以上字符时,比如 ababa 这个字符记作串 1,把两边的 a 去掉,也就是 bab 记作串 2,可以看出只要串2是一个回文串,那么左右各多了一个 a 的串 1 必定也是回文串。所以当 s.charAt(l) == s.charAt(r) 时,自然要看 dp[l+1][r-1] 是不是一个回文串。

实现代码

class Solution {
    public int countSubstrings(String s) {
        int len = s.length();
        if(len == 0){
            return 0;
        }
        boolean[][] dp = new boolean[len][len];
        int count = 0;
        for(int r = 0; r < len; r++){
            for(int l = 0; l <= r; l++){
                if(s.charAt(l) == s.charAt(r) && (r - l < 2 || dp[l+1][r-1])){
                    dp[l][r] = true;
                    count++;
                }else{
                    dp[l][r] = false;
                }
            }
        }
        return count;
    }
}

优化

用一维dp数组标记状态实现动态规划
class Solution {
    public int countSubstrings(String s) {
        int length = s.length();
        boolean[] dp = new boolean[length];
        int count = 0;
        for (int j = 0; j < length; j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j) && (j - i <= 2 || dp[i + 1])) {
                    dp[i] = true;
                    count++;
                } else {
                    dp[i] = false;
                }
            }
        }
        return count;
    }
}

复杂度分析

  • 时间复杂度:

NC17 + LC5:最长回文子串

题目地址:

解法一:暴力解法

优化:既然要取最长的,就从长到短判断其子串,遇到就直接截取返回,这样可大大减少时间,对于“cccccc...全是同一个元素组成的这种字符串,也能最早判断出来
class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        for(int i = len; i > 0; i--){ //先确定末尾
            for(int j = 0; j <= len-i; j++){
                if(isPalindrome(s, j, j+i-1)){ //从长到短进行判断,是回文就直接截取返回
                    return s.substring(j, j + i);
                }
            }
        }
        return String.valueOf(s.charAt(0));
    }
    public boolean isPalindrome(String str, int left, int right){ //判断字符串是否是回文
        while(left < right){
            if(str.charAt(left++) != str.charAt(right--)){
                return false;
            }
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度:
  • 空间复杂度:

解法二:中心扩散法

思路

  • 该解法是在题目647.回文子串解法的基础上改动得来,在进行中心扩散的过程中,不断截取当前的子串,比较长度,更新当前的最长回文子串
  • 这种做法显然不是最好做法,每次扩散都需要截取,耗费时间

实现代码

class Solution {
    public String longestPalindrome(String s) {
        String res = "";
        int len = s.length();
        for(int i = 0; i < len*2-1; i++){
            int left = i / 2;
            int right = left + i % 2;
            while(left >= 0 && right < len && s.charAt(left) == s.charAt(right)){
                String pal = s.substring(left, right+1);
                if(pal.length() > res.length()){
                    res = pal;
                }
                left--;
                right++;
            }
        }
        return res;
    }
}

优化:通过start和end记录回文子串的开始和结束下标,最后再进行截取,这样大大降低了时间复杂度,推荐做法

class Solution {
     public static String longestPalindrome(String s) {
        if (s == null || s.length() < 1) {
            return "";
        }
        int start = 0, end = 0; //标记最长回文子串的开头和结尾下标
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i); //以i为中心进行扩散求回文子串长度
            int len2 = expandAroundCenter(s, i, i + 1); //以i和i+1为中心
            int len = Math.max(len1, len2);
            if (len > end - start + 1) { //大于就根据长度和i更新start和end
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1); //根据start和end进行回文子串的截取
    }

    public static int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }
        return right - left - 1;
    }
}

解法三:动态规划

  • 在LC 647.回文子串的动态规划解法的基础上进行修改
  • 为回文子串就进行判断,更新当前回文子串的长度最大值以及记录该回文子串开始的位置
  • 最后根据长度最大值以及开始位置截取字符串进行返回
class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        boolean[][] dp = new boolean[len][len];
        int start = 0; //最长回文子串的开始位置
        int max = 1; //最长回文串的长度
        for(int r = 0; r < len; r++){
            for(int l = 0; l <= r; l++){
                if(s.charAt(l) == s.charAt(r) && (r - l < 2 || dp[l+1][r-1])){ //是回文子串就标记并判断
                    dp[l][r] = true;
                    if((r - l + 1) > max){ //大于就更新位置与长度
                        start = l;
                        max = r - l + 1;
                    }
                }else{
                    dp[l][r] = false;
                }
            }
        }
        return s.substring(start, start+max);
    }
}

全部评论

相关推荐

11-15 17:19
湖南大学 Java
成果成果成果果:这是哪个公司的hr,这么离谱吗,我没见过用性别卡技术岗的,身边女性同学拿大厂offer的比比皆是
点赞 评论 收藏
分享
评论
点赞
1
分享
牛客网
牛客企业服务