zoukankan      html  css  js  c++  java
  • [leetcode 周赛 148] 1147 段式回文

    1147 Longest Chunked Palindrome Decomposition 段式回文

    描述

    段式回文 其实与 一般回文 类似,只不过是最小的单位是 一段字符 而不是 单个字母
    举个例子,对于一般回文 "abcba" 是回文,而 "volvo" 不是,但如果我们把 "volvo" 分为 "vo"、"l"、"vo" 三段,则可以认为 “(vo)(l)(vo)” 是段式回文(分为 3 段)。
    给你一个字符串 text,在确保它满足段式回文的前提下,请你返回 段 的 最大数量 k。
    如果段的最大数量为 k,那么存在满足以下条件的 a_1, a_2, ..., a_k

    每个 a_i 都是一个非空字符串;

    将这些字符串首位相连的结果 a_1 + a_2 + ... + a_k 和原始字符串 text 相同;
    对于所有1 <= i <= k,都有 a_i = a_{k+1 - i}

    • 示例1

    输入:text = "ghiabcdefhelloadamhelloabcdefghi"
    输出:7
    解释:我们可以把字符串拆分成 "(ghi)(abcdef)(hello)(adam)(hello)(abcdef)(ghi)"。

    • 示例2

    输入:text = "merchant"
    输出:1
    解释:我们可以把字符串拆分成 "(merchant)"。

    • 示例3

    输入:text = "antaprezatepzapreanta"
    输出:11
    解释:我们可以把字符串拆分成 "(a)(nt)(a)(pre)(za)(tpe)(za)(pre)(a)(nt)(a)"。

    • 示例4

    输入:text = "aaa"
    输出:3
    解释:我们可以把字符串拆分成 "(a)(a)(a)"。

    • 提示

    text 仅由小写英文字符组成。
    1 <= text.length <= 1000

    思路

    • 两边字段定位 [i, j) --> [n-j-1, n-i-1)

    可以有两种方式实现:

    1. 暴力遍历
    2. String.substring(beginIndex, endIndex) 注意字段范围[beginIndex, endIndex)
    • 进入下一轮判断
      有两种方式:递归/迭代

    递归: (0, n-1) --> (0, s)==(e,n-1) (s,e) -->

    开始时, 起始为0, 终止为n-1
    遍历0~n-1, 找到(0, s)==(e,n-1)
    此时设起始为s, 终止为e 开始下一轮

    迭代: i -> n/2 --> (j, i) -->

    开始遍历 1~n/2 枚举为终止位置i
    选取j=0 作为开始位置
    找到(j, i)==(n-i-1, n-j-1)
    设j=i 作为开始位置 开始下一循环

    代码实现

    贪心算法

    每次两端有相同的字段就将其分词, 并投入到下一轮递归中, 直至字符串为空或者无法进行分词为止

    class Solution {
        public int longestDecomposition(String text) {
            if (null == text) return -1;
            
            int n = text.length();
            // 将字符串分成 [0,i) ~ [n-i, n)
            // 如果上面相等 表示有2份回文字段
            // 并将剩余字段投入下一轮迭代 [i, n-i)
            // i 其实表示的是比较字段的长度
            for (int i = 1; i <= n/2; i++) {
                if (text.substring(0, i).equals(text.substring(n-i, n))) {
                    return 2 + longestDecomposition(text.substring(i, n-i));
                }
            }
            
            // 当最终剩余字段大于零 则其单独成一段 +1
            return n == 0 ? 0 : 1;
        }
    }
    

    动态规划(使用二维数组)

    class Solution {
        int[][] dp;
        String text;
        
        public int longestDecomposition(String text) {
            int n = text.length();
            dp = new int[n][n];
            
            // 初始化为-1 用以表示该位置是否被处理
            for (int i = 0; i < n; i++) Arrays.fill(dp[i], -1);
            this.text = text;
            
            // 求整个字符串的回文字段k
            return helper(0, n-1);
        }
        
        /**
         * 求回文字段数 text的子串
         * @param s 起始位置
         * @param e 终止位置
         * @return 该字段的回文字段数
         **/
        int helper(int s, int e) {
            // 非法字段 起始位置大于终止位置
            if (s > e) return 0;
            // 特殊情况 刚好只剩下一个字符
            if (s == e) return dp[s][e] = 1;
            // 不为-1 则表示以及处理过 直接拿来用就可以
            if (dp[s][e] != -1) return dp[s][e];
            
            // 字符串本身算一回文字段
            int res = 1;
            // 枚举长度 1 ~ (e-s+1)/2 从s开始的子串
            for (int l = 1; l <= (e-s+1)/2; l++) {
                String st = text.substring(s, s+l);
                String ed = text.substring(e-l+1, e+1);
                // 如果两子串相等 表示找到回文字段 +2
                if (st.equals(ed)) {
                    int tmp = helper(s+l, e-l);
                    // 更新结果值
                    res = tmp+2;
                }
            }
            
            return dp[s][e] = res;
        }
    }
    

    动态规划(使用一维数组)

    class Solution {
        // 存储表示0~i 出现的回文字段
        int dp[] = new int[1050];
        
        public int longestDecomposition(String text) {
            int n = text.length(), ans = 1;
            char[] chs = text.toCharArray();
            
            // dp数组初始化
            Arrays.fill(dp, -1);
            // 肯定是有从0开始的回文字段
            dp[0] = 0;
            
            // 表示最新的子串开始位置
            int left = 0;
            // i 表示终止位置 (j, i)
            for (int i = 1; i <= n/2; i++) {
                for (int j = left; j < i; j++) {
                    // 可不可以作为起始点
                    if (dp[j] == -1) continue;
                    // [j, i-1]是否可以作为回文字段
                    if (!check(chs, j, i, n)) continue;
                    // 如果可以作为回文字段
                    // [0,i) 有多少回文字段
                    dp[i] = dp[j]+1;
                    // 更新最新子串开始位置
                    left = i;
                }
            }
    
            // 如果最终处理字段的位置 并没有遍历完字符串(n/2) 表示还有剩余1字段可以作为回文字段
            return Math.max(dp[left]*2 + (left*2<n?1:0), ans);
        }
        
        /**
         * 检查[j,i-1]段前后是否相等
         * 暴力遍历
         **/
        boolean check(char[] chs, int j, int i, int n) {
            // j ~ i-1 --> n-i ~ n-j-1
            for (int ii = j; ii < i; ii++) {
                if (chs[ii] != chs[n-i-j+ii]) return false;
            }
            return true;
        }
    }
    
  • 相关阅读:
    我的友情链接
    我的友情链接
    BUMO:超级节点共建接口
    OpenStack 对接 Ceph 环境可以创建卷但不能挂载卷的问题
    零知识证明: 抛砖引玉
    比特币原理
    区块链零知识证明:STARKs,Part-3:攻坚(下)
    什么是zkSNARKs:谜一般的“月亮数学”加密,Part-1
    什么是zkSNARKs:谜一般的“月亮数学”加密,Part-2
    STARKs, Part I: 多项式证明
  • 原文地址:https://www.cnblogs.com/slowbirdoflsh/p/11312365.html
Copyright © 2011-2022 走看看