LeetCode

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

2020-02-27  本文已影响0人  桐桑入梦

给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。
请你返回让 s 成为回文串的 最少操作次数 。
「回文串」是正读和反读都相同的字符串。

示例 1:
输入:s = "zzazz"
输出:0
解释:字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。

示例 2:
输入:s = "mbadm"
输出:2
解释:字符串可变为 "mbdadbm" 或者 "mdbabdm" 。

示例 3:
输入:s = "leetcode"
输出:5
解释:插入 5 个字符后字符串变为 "leetcodocteel" 。

示例 4:
输入:s = "g"
输出:0

示例 5:
输入:s = "no"
输出:1

提示:

1 <= s.length <= 500
s 中所有字符都是小写字母。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-insertion-steps-to-make-a-string-palindrome

第一次尝试

class Solution {
    public int minInsertions(String s){

        String rs = new StringBuilder(s).reverse().toString();
        int length = s.length();
        int[][] times = new int[length+1][length+1];

        //计算s和倒序的rs的最长公共子串的长度
        for(int i=1;i<=length;i++) {
            for(int j=1;j<=length;j++) {
                if(s.charAt(i-1) == rs.charAt(j-1))
                    times[i][j] = times[i-1][j-1]+1;
                else
                    times[i][j] = max(times[i-1][j],times[i][j-1]);
            }
        }
        
        int result = length; //初始为最大值
        //分界线是任意的两个元素之间,可以左边或者右边没有元素
        for(int i=0;i<=length;i++){ //处理最终回文串长度为偶数的情况
            int minInsert = length - 2*times[i][length-i];
            result = min(result,minInsert);
        } 

        //分界线是某一个元素,这个元素必须存在,这个元素的左边和右边构成对称
        for(int i=1;i<=length;i++){ //处理最终回文串长度为奇数的情况
            int minInsert = (length-1)-2*times[i-1][length-i];
            result = min(result,minInsert);
        }   
        return result;
    }
    
    int max(int a,int b) { return a > b ? a : b; }  
    int min(int a,int b) { return a > b ? b : a; }
}

第二次尝试:

class Solution {
    public int minInsertions(String s){

        String rs = new StringBuilder(s).reverse().toString();
        int length = s.length();
        int[][] times = new int[length+1][length+1];

        //计算s和倒序的rs的最长公共子串的长度
        for(int i=1;i<=length;i++) {
            for(int j=1;j<=length-i;j++) {
                if(s.charAt(i-1) == rs.charAt(j-1))
                    times[i][j] = times[i-1][j-1]+1;
                else
                    times[i][j] = max(times[i-1][j],times[i][j-1]);
            }
        }
        
        int result = length; //初始为最大值
        //分界线是任意的两个元素之间,可以左边或者右边没有元素
        for(int i=0;i<=length;i++){ //处理最终回文串长度为偶数的情况
            int minInsert = length - 2*times[i][length-i];
            result = min(result,minInsert);
        } 

        //分界线是某一个元素,这个元素必须存在,这个元素的左边和右边构成对称
        for(int i=1;i<=length;i++){ //处理最终回文串长度为奇数的情况
            int minInsert = (length-1)-2*times[i-1][length-i];
            result = min(result,minInsert);
        }   
        return result;
    }
    
    int max(int a,int b) { return a > b ? a : b; }  
    int min(int a,int b) { return a > b ? b : a; }
}

第三次尝试:

class Solution {
    public int minInsertions(String s){
        int length = s.length();
        int[][] times = new int[length+1][length+1];

        //计算s和倒序的rs的最长公共子串的长度
        for(int i=1;i<=length;i++) {
            for(int j=1;j<=length-i;j++) {
                if(s.charAt(i-1) == s.charAt(length-j))
                    times[i][j] = times[i-1][j-1]+1;
                else
                    times[i][j] = max(times[i-1][j],times[i][j-1]);
            }
        }
        
        int result = length; //初始为最大值
        //分界线是任意的两个元素之间,可以左边或者右边没有元素
        for(int i=0;i<=length;i++){ //处理最终回文串长度为偶数的情况
            int minInsert = length - 2*times[i][length-i];
            result = min(result,minInsert);
        } 

        //分界线是某一个元素,这个元素必须存在,这个元素的左边和右边构成对称
        for(int i=1;i<=length;i++){ //处理最终回文串长度为奇数的情况
            int minInsert = (length-1)-2*times[i-1][length-i];
            result = min(result,minInsert);
        }   
        return result;
    }
    
    int max(int a,int b) { return a > b ? a : b; }  
    int min(int a,int b) { return a > b ? b : a; }
}

第四次尝试:

class Solution {
    public int minInsertions(String s){
        int length = s.length();
        int[][] dp = new int[length+1][length+1];

        for(int i=1;i<=length;i++){
            for(int j=1;j<=length;j++){
                if(s.charAt(i-1)==s.charAt(length-j)) dp[i][j] = dp[i-1][j-1]+1;
                else dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
            }
        }
        int result = length - dp[length][length];
        return result;
    }
    
    int max(int a,int b) { return a > b ? a : b; }  
}

第五次尝试:这次和之前使用的方法不同,枚举回文串的长度

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

        //长度为1的时候一定是回文串,因此len从2开始枚举
        for(int len = 2;len<=length;len++){
            for(int i = 0;i+len<=length;i++){
                int j = i + len - 1;
                if(s.charAt(i) == s.charAt(j)) dp[i][j]=dp[i+1][j-1];
                else dp[i][j] = min(dp[i+1][j],dp[i][j-1])+1;
            }
        }
        return dp[0][length-1];
    } 

    private int min(int a,int b){ return a>b ? b : a; }
}
leetcode也是提供这样的方法,上面的方法使用了数组内存消耗较大,如图

试试滚动数组?

class Solution {
    public int minInsertions(String s){
        int length = s.length();
        int[][] dp = new int[2][length+1];
        int k = 1;  //k^1表示上一次记录的数据
        for(int i=1;i<=length;i++){
            for(int j=1;j<=length;j++){
                if(s.charAt(i-1)==s.charAt(length-j)) dp[k][j] = dp[k^1][j-1]+1;
                else dp[k][j] = max(dp[k^1][j],dp[k][j-1]);
            }
            k^=1;
        }
        int result = length - dp[k^1][length];
        return result;
    }
    
    int max(int a,int b) { return a > b ? a : b; }  
}

然而效果并不是很明显!!!
有推荐方法的请指教一下(能超过90%的那种)

总结:
【方法1】:把字符串s分成s1和s2两部分,寻找s1和rev(s2)的最长公共串.
【方法2】:找到字符串s的最长回文串s',然后|s|-|s'|就是剩下的不能组成回文串的字符数,那个添加同样数量的字符使得s成为回文串.
【方法3】:dp[i][j]记录长度为len的字符串通过添加字符构造成回文串需要添加id最少字符,这里len = j-i+1

上一篇下一篇

猜你喜欢

热点阅读