动态规划-回文子串问题

发布于:2024-05-02 ⋅ 阅读:(31) ⋅ 点赞:(0)

文章目录

  • 1. 回文子串(647)
  • 2. 最长回文子串(5)
  • 3. 分割回文串 IV(1745)
  • 4. 分割回文串 II(132)
  • 5. 最长回文子序列(516)
  • 6. 让字符串成为回文串的最少插入次数(1312)


1. 回文子串(647)

题目描述:
在这里插入图片描述

状态表示:
设置一个布尔类型二维数组dp,使用dp[i][j]来表示在i和j这个闭区间内的子串是否为回文子串。
状态转移方程:
当i和j位置的元素相同时分为三种情况,第一种是i等于j,第二种就是i+1等于j,第三种是就是除了i和j位置的元素还包含中间很多元素,那么dp[i][j]=dp[i+1][j-1]。
初始化:
这里无需初始化,都赋为false即可。
填表顺序:
填表要使用到两层循环,从左至右,从上到下。
返回值:
返回值就是返回dp数组中为true的个数。
代码如下:

class Solution {
    public int countSubstrings(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];

        int ret = 0;
        for (int j = 0; j < n; j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j)) {
                    if (i == j) {
                        dp[i][j] = true;
                    } else if (i + 1 == j) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                    if (dp[i][j]) {
                        ret++;
                    }

                }

            }

        }

        return ret;
    }
}

题目链接
时间复杂度:O(N^2)
空间复杂度:O(N^2)

2. 最长回文子串(5)

题目描述:
在这里插入图片描述
状态表示:
这题状态表示和上一题一致。
状态转移方程:
这题状态转移方程和上一题也是一致的。
初始化:
初始化就是全是false,即不用初始化。
填表顺序:
从左至右,从上到下。
返回值:
返回dp数组中值为true的元素的i和j坐标差值为最大值的子串。
代码如下:

 class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();

        boolean[][] dp = new boolean[n][n];

        int startIndex = 0;
        int endIndex = 0;
        int max = Integer.MIN_VALUE;
        for (int j = 0; j < n; j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j)) {
                    if (i == j) {
                        dp[i][j] = true;
                    } else if (i + 1 == j) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                if (dp[i][j] && j - i + 1 > max) {
                    max = j - i + 1;
                    startIndex = i;
                    endIndex = j;
                }
            }
        }
        return s.substring(startIndex, endIndex + 1);
    }
}

题目链接
时间复杂度:O(N^2)
空间复杂度:O(N^2)

3. 分割回文串 IV(1745)

题目描述:
在这里插入图片描述

状态表示:
这题的状态表示与前两题一致。
状态转移方程:
与前两题一致。
初始化:
与前两题一致。
填表顺序:
与前两题一致。
返回值:
这题的返回值有点特殊,需要使用两层循环,将两层循环的下标用来分割字符串s并且判断分割好的三个子串在dp表中的值是否为true,如果都为true最终返回就是true,如果遍历完成没有一次是true,那么就返回false。
代码如下:

class Solution {
    public boolean checkPartitioning(String s) {
        int n = s.length();

        boolean[][] dp = new boolean[n][n];

        for (int j = 0; j < n; j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j)) {
                    if (i == j) {
                        dp[i][j] = true;
                    } else if (i + 1 == j) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

            }
        }

        for (int i = 0; i < n - 1; i++) {
            for (int j = i; j < n - 1; j++) {
                if (dp[0][i] && dp[i + 1][j] && dp[j + 1][n - 1]) {
                    return true;
                }
            }
        }

        return false;

    }
}

题目链接
时间复杂度:O(N^2)
空间复杂度:O(N^2)

4. 分割回文串 II(132)

题目描述:
在这里插入图片描述

状态表示:
这里有两个动态数组,第一个sup二维数组用于辅助运算,思想和前面几题类似,就是判断i到j位置是否为一个回文子串。第二个一维的动态数组dp,dp[i]表示以i位置元素为结尾的子串可以被分割为多个回文子串的最小次数。
状态转移方程:
对于二位数组的状态转移方程我们不多赘述,前面几题用很多。对于一维数组的dp[i],如果0~i是一个回文子串,那么dp[i]的值就是0,另一种情况就是0到i不是一个回文子串,那么我们固定住i,利用一个循环来遍历i前面的元素使用j来表示,如果j到i是一个回文子串,那么更新dp[i]=Math.min(dp[i],dp[j-1]+1)。
初始化:
为了不影响运算,将dp数组全初始化为最大值。
填表顺序:
dp数组从左至右。
返回值:
因为要返回整个字符串的最小分割次数,所以直接返回dp[n-1]即可。
代码如下:

class Solution {
    public int minCut(String s) {
        int n = s.length();

        boolean[][] sup = new boolean[n][n];
        for (int j = 0; j < n; j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j)) {
                    if (i == j) {
                        sup[i][j] = true;
                    } else if (i + 1 == j) {
                        sup[i][j] = true;
                    } else {
                        sup[i][j] = sup[i + 1][j - 1];
                    }
                }

            }

        }

        int[] dp = new int[n];
        for (int i = 0; i < n; i++) {
            dp[i] = Integer.MAX_VALUE;
        }

        for (int i = 0; i < n; i++) {
            if (sup[0][i]) {
                dp[i] = 0;
            } else {
                for (int j = i; j >= 1; j--) {
                    if (sup[j][i]) {
                        dp[i] = Math.min(dp[i], dp[j - 1] + 1);
                    }
                }

            }

        }
        return dp[n - 1];
    }
}

题目链接
时间复杂度:O(N^2)
空间复杂度:O(N^2)

5. 最长回文子序列(516)

题目描述:
在这里插入图片描述

状态表示:
和第二题一致。
状态转移方程:
和第二题一致。
初始化:
和第二题一致。
填表顺序:
和第二题一致。
返回值:
这题和第二题就是一样的题目,只不过第二题要返回字符串,但是这里返回的只是长度。
代码如下:

class Solution {
    public int longestPalindromeSubseq(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];

        int max = 1;
        for (int j = 0; j < n; j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j)) {
                    if (i == j) {
                        dp[i][j] = true;
                    } else if (i + 1 == j) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }
                if (dp[i][j] && j - i + 1 > max) {
                    max = j - i + 1;
                }
            }

        }

        return max;

    }
}

题目链接
时间复杂度:O(N^2)
空间复杂度:O(N^2)

6. 让字符串成为回文串的最少插入次数(1312)

题目描述:
在这里插入图片描述

状态表示:
使用二位数组dp[i][j]表示在i和j区间的子串变成回文子串的最小插入次数。
状态转移方程:
分为两种情况,第一种i和j位置元素相等时,当i+1等于j时,dp[i][j]=0,当i等于j时,dp[i][j]=0,当i+1<j时,dp[i][j]=dp[i+1][j-1]。第二种情况当i和j位置元素不相等时,需要插入元素所以dp[i][j]=Math.min(dp[i+1][j],dp[i][j-1])+1;
初始化:
无需初始化。
填表顺序:
从下到上,从左至右。
返回值:
返回0到n-1的原字符串的最小分割次数即dp[0][n-1]。
代码如下:

class Solution {
    public int minInsertions(String s) {
        int n = s.length();

        int[][] dp = new int[n][n];

        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s.charAt(i) == s.charAt(j)) {
                    if (i == j) {
                        dp[i][j] = 0;
                    } else if (i + 1 == j) {
                        dp[i][j] = 0;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                } else {
                    dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1;
                }

            }
        }

        return dp[0][n - 1];
    }
}

题目链接
时间复杂度:O(N^2)
空间复杂度:O(N^2)