LeetCode——1745. 回文串分割 IV(Palindrome Partitioning IV)——分析及代码(Java)

LeetCode——1745. 回文串分割 IV[Palindrome Partitioning IV]——分析及代码[Java]

一、题目

给你一个字符串 s ,如果可以将它分割成三个 非空 回文子字符串,那么返回 true ,否则返回 false 。
当一个字符串正着读和反着读是一模一样的,就称其为 回文字符串 。

示例 1:

输入:s = "abcbdd"
输出:true
解释:"abcbdd" = "a" + "bcb" + "dd",三个子字符串都是回文的。

示例 2:

输入:s = "bcbddxy"
输出:false
解释:s 没办法被分割成 3 个回文子字符串。

提示:

  • 3 <= s.length <= 2000
  • s 只包含小写英文字母。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-partitioning-iv
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、分析及代码

1. 两端枚举

(1)思路

因为第 1 和第 3 个子字符串的一端边界固定为字符串首尾,可先求出其另一端可能的取值,再枚举尝试中间的第 2 个字符串是否构成回文子字符串,直至找到可行解或完成遍历。

(2)代码

class Solution {
    
    
    public boolean checkPartitioning(String s) {
    
    
        char[] c = s.toCharArray();
        int len = s.length();
        List<Integer> l1list = new ArrayList<>();
        List<Integer> l2list = new ArrayList<>();
        for (int l1 = 1; l1 < len - 1; l1++) {
    
    
            if (check(c, 0, l1 - 1))
                l1list.add(l1);
        }
        for (int l2 = len - 1; l2 > 1; l2--) {
    
    
            if (check(c, l2, len - 1))
                l2list.add(l2);
        }
        
        for (int i = 0; i < l1list.size(); i++) {
    
    
            for (int j = 0; j < l2list.size(); j++) {
    
    
                if (l1list.get(i) < l2list.get(j) && check(c, l1list.get(i), l2list.get(j) - 1))
                    return true;
            }
        }
        return false;
    }
    
    public boolean check(char[] c, int l, int r) {
    
    
        while (l < r) {
    
    
            if (c[l++] != c[r--])
                return false;
        }
        return true;
    }
}

(3)结果

执行用时 :388 ms,在所有 Java 提交中击败了 5.05% 的用户;
内存消耗 :37.1 MB,在所有 Java 提交中击败了 97.01% 的用户。

2. 动态规划

(1)思路

设计一个二维布尔数组 dp[i][j],记录下标为 [i,j] 的子字符串是否为回文字符串。因为回文子字符串只可能由对称中心向两侧延伸得到,可先计算得到所有回文子字符串,再遍历查找是否可将原字符串分割为 3 个回文子字符串。

(2)代码

class Solution {
    
    
    public boolean checkPartitioning(String s) {
    
    
        char[] c = s.toCharArray();
        int len = s.length();
        boolean[][] dp = new boolean[len][len];
        for (int i = 0; i < len; i++)
            for (int j = 0; j < len; j++)
                dp[i][j] = false;
        for (int i = 0; i < len; i++) {
    
    //单个字母及延伸的回文字符串
            int halflen = 0;
            while (i - halflen >= 0 && i + halflen < len && c[i - halflen] == c[i + halflen])
                dp[i - halflen][i + halflen++] = true;
        }
        for (int i = 1; i < len; i++) {
    
    //相同的2个字母及延伸的回文字符串
            if (c[i - 1] == c[i]) {
    
    
                int halflen = 0;
                while (i - 1 - halflen >= 0 && i + halflen < len && c[i - 1 - halflen] == c[i + halflen])
                    dp[i - 1 - halflen][i + halflen++] = true;
            }
        }

        for (int l = 1; l < len - 1; l++) {
    
    //l,r表示分割点,区间左闭右开
            if (dp[0][l - 1])
                for (int r = l + 1; r < len; r++)
                    if (dp[l][r - 1] && dp[r][len - 1])
                        return true;
        }
        return false;
    }
};

(3)结果

执行用时 :84 ms,在所有 Java 提交中击败了 68.28% 的用户;
内存消耗 :42.2 MB,在所有 Java 提交中击败了 65.52% 的用户。

三、其他

本题还可结合 Manacher 方法,在线性时间复杂度内求解。

猜你喜欢

转载自blog.csdn.net/zml66666/article/details/113817726