分割回文串 III

给你一个由小写字母组成的字符串 s,和一个整数 k。

请你按下面的要求分割字符串:

首先,你可以将 s 中的部分字符修改为其他的小写英文字母。
接着,你需要把 s 分割成 k 个非空且不相交的子串,并且每个子串都是回文串。
请返回以这种方式分割字符串所需修改的最少字符数。

 

示例 1:

输入:s = "abc", k = 2
输出:1
解释:你可以把字符串分割成 "ab" 和 "c",并修改 "ab" 中的 1 个字符,将它变成回文串。
示例 2:

输入:s = "aabbc", k = 3
输出:0
解释:你可以把字符串分割成 "aa"、"bb" 和 "c",它们都是回文串。
示例 3:

输入:s = "leetcode", k = 8
输出:0
 

提示:

1 <= k <= s.length <= 100
s 中只含有小写英文字母。

 

class Solution {

    public int palindromePartition(String s, int k) {

        List<List<String>> lists = new ArrayList<>();

        if (k == 1) {

            List<String> dd = new ArrayList<>();

            dd.add(s);

            lists.add(dd);

        } else {

            lists = splitString(s,k-1);

        }

        int min = s.length();

        for (List<String> list : lists) {

            int c = 0;

            for (String s1 : list) {

                for (int i = 0;i<s1.length()/2;i++) {

                    if (s1.charAt(i) != s1.charAt(s1.length() - i - 1)) {

                        c++;

                    }

                }

            }

            min = Math.min(min,c);

        }

        return min;

    }

 

    public List<List<String>> splitString(String s, int m) {

        List<List<String>> lists = new ArrayList<>();

        if (m == 1) {//当m次数为1时不再分割,返回分割的可能

            for (int i = 0;i < s.length() - 1; i++) {

                List<String> list = new ArrayList<>();

                list.add(s.substring(0,i+1));

                list.add(s.substring(i+1,s.length()));

                lists.add(list);

            }

            return lists;

        } else {//分割后段的长度必须大于等于分割次数

            for (int i = s.length() - m;i > 0;i--) {

                List<List<String>> lastList = splitString(s.substring(i,s.length()),m-1);//递归

                for (List<String> strings : lastList) {

                    List<String> list = new ArrayList<>();

                    list.add(s.substring(0,i));

                    list.addAll(strings);

                    lists.add(list);

                }

            }

        }

        return lists;

    }

}

 

要点

1.采用暴力递归法,先获得一个字符串s分割成k个字符串可能产生的所有情况存入链表

2.再遍历每个链表 计算每种可能需要修改字符的次数

3.但是字符过长,分割k的次数过大时时间消耗过长,不建议采用这个方法

 

  第二次改进

private Map<String, Map<Integer, Integer>> memory = new HashMap<>();

public int palindromePartition(String s, int k) {

        int min = s.length();

        if (k == 1) {

            min = toS(s);

        } else {

            min = splitString(s,k-1);

        }

        return min;

    }

 

    public int splitString(String s, int m) {

        List<List<String>> lists = new ArrayList<>();

int min = s.length();
if (memory.get(s) != null) {
   if (memory.get(s).get(m) != null) {
      min = memory.get(s).get(m);
   }
}

        if (m == 1) {//当m次数为1时不再分割,返回s

            for (int i = 0;i < s.length() - 1; i++) {

                List<String> list = new ArrayList<>();

                list.add(s.substring(0,i+1));

                list.add(s.substring(i+1,s.length()));

                lists.add(list);

                min = Math.min(toS(s.substring(0,i+1)) + toS(s.substring(i+1,s.length())),min);

            }

        } else {//分割后段的长度必须大于等于分割次数

            for (int i = s.length() - m;i > 0;i--) {

                min = Math.min(min,toS(s.substring(0,i)) + splitString(s.substring(i,s.length()),m-1));

            }

        }

Map<Integer,Integer> map = new HashMap<>();
map.put(m,min);
memory.put(s,map);

        return min;

    }

 

    public int toS(String s1) {

        int c = 0;

        for (int i = 0;i<s1.length()/2;i++) {

            if (s1.charAt(i) != s1.charAt(s1.length() - i - 1)) {

                c++;

            }

        }

        return c;

    }

要点:任采用递归,但增加记忆法用map存储s字段被分割成k次时需要的次数节省少量时间,在递归的时候时直接返回多个可能中的需修改次数最小值,节省时间,但还是慢

 

动态规划分割回文串是一种常用的解决方案。在动态规划中,我们可以使用不同的状态定义和状态转移方程来解决这个问题。 一种常见的状态定义是使用一维数组dp[i],其中dp[i]表示字符串s的前i个字符形成回文子串的最少分割次数。这种定义可以通过判断s[j:i]是否为回文来进行状态转移,其中1 <= j <= i。具体的状态转移方程可以如下表示: - 当s[0:i]本身就是一个回文串时,不需要进行分割,即dp[i] = 0。 - 否则,我们可以遍历所有可能的分割点j,如果s[j+1:i]是回文串,那么我们可以将问题分割为两部分,即dp[i] = min(dp[i], dp[j] + 1)。 另一种状态定义是使用二维数组dp[i][j],其中dp[i][j]表示字符串s的前i个字符分割为j个子串的修改的最小字符数。在这种定义下,我们可以使用类似的状态转移方程来进行计算。具体的状态转移方程可以如下表示: - 当i < j时,不可能将前i个字符分割为j个子串,即dp[i][j] = INF。 - 当i >= j时,我们可以遍历所有可能的分割点k,计算dp[i][j]的最小值,即dp[i][j] = min(dp[i][j], dp[k][j-1] + cost(k+1, i)),其中cost(k+1, i)表示将子串s[k+1:i]修改为回文所需的最小字符数。 这两种定义和状态转移方程都可以用来解决动态规划分割回文串的问题,具体使用哪种方法取决于具体的问题要求和效率要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [动态规划解决回文串问题](https://blog.csdn.net/qq_37414405/article/details/111317301)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [动态规划解分割回文串](https://blog.csdn.net/melody157398/article/details/119769501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值