leetcode
leetcode 1701 ~ 1750
将句子分隔成行的最低成本

将句子分隔成行的最低成本

难度:

标签:

题目描述

代码结果

运行时间: 36 ms, 内存: 16.2 MB


/*
 * 题目思路:
 * 给定一个句子和一个最大行宽度,将句子分隔成多行,要求每行的宽度尽可能均匀,最小化宽度与平均宽度的偏差。
 * 使用Java Stream方法,首先计算每个单词在不同位置分割的最小成本。
 * 然后使用Stream方法进行处理和计算。
 */
import java.util.*;
import java.util.stream.*;

public class SentenceSplitterStream {
    public int minCostToSplit(String sentence, int maxWidth) {
        String[] words = sentence.split(" ");
        int n = words.length;
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[n] = 0;

        IntStream.range(0, n).boxed().sorted(Comparator.reverseOrder()).forEach(i -> {
            int lineLength = 0;
            for (int j = i; j < n; j++) {
                lineLength += words[j].length();
                if (j > i) lineLength++; // 加上空格
                if (lineLength > maxWidth) break;
                int cost = (maxWidth - lineLength) * (maxWidth - lineLength);
                if (j == n - 1) cost = 0;
                dp[i] = Math.min(dp[i], cost + dp[j + 1]);
            }
        });
        return dp[0];
    }

    public static void main(String[] args) {
        SentenceSplitterStream splitter = new SentenceSplitterStream();
        String sentence = "This is a sample sentence for splitting";
        int maxWidth = 10;
        System.out.println(splitter.minCostToSplit(sentence, maxWidth));
    }
}

解释

方法:

该题解采用动态规划方法来解决问题。首先,通过`endings`数组计算每个词到句首的累积长度,包括空格。`dp[i]`表示从单词i开始到句子末尾的最小成本。从句子的末尾开始计算dp值,逐步向前推进。如果从当前单词i到句尾的所有单词长度和小于等于k,则dp[i]为0(因为所有单词都可以放在一行)。否则,需要计算将单词分成多行时的最小成本,即考虑每一个可能的断点j,计算从i到j的单词放在一行时的剩余空间的平方(作为成本),加上从j+1开始的最小成本。最终,dp[0]将给出整个句子的最低分隔成本。

时间复杂度:

O(N^2)

空间复杂度:

O(N)

代码细节讲解

🦆
在动态规划中,`dp[i]`数组是如何初始化的,为什么初始值设置为无穷大?
在动态规划中,`dp[i]`数组用于存储从第i个单词开始到句子末尾的最小成本。初始化为无穷大是因为在计算之前,我们不知道具体的成本值,而无穷大可以保证在后续的最小值比较过程中,任何实际计算出的成本都会被选为更小值。这是一种常见的动态规划初始化方法,用以确保正确地更新成本值。
🦆
如何确保`endings`数组正确计算了每个词到句首的累积长度,包括空格?
`endings`数组通过累加每个单词的长度及其之前的空格来构建。初始化`endings[0]`为-1,因为第一个单词前没有空格。对于每个单词,`endings[i+1]`等于`endings[i]`加上当前单词的长度加1(代表单词前的空格)。这样,`endings`数组能够准确反映从句首到当前单词的累积长度,包括单词间的空格。
🦆
循环中使用的`takewhile`函数是如何工作的,它在这里起到了什么作用?
`takewhile`函数从一个迭代器中取元素直到指定的条件不再满足。在这个算法中,它用来迭代所有可能的断点j,条件是单词i到单词j的长度总和(包括空格)不超过k。这样,使用`takewhile`可以有效地限制循环范围,避免不必要的计算,提高效率。
🦆
算法在处理边界情况,如单个单词长度超过k,或所有单词长度和刚好等于k时的表现如何?
当单个单词长度超过k时,由于在计算dp时检查了从i到N的长度是否小于等于k,若没有满足这一条件的j存在,dp[i]将保持为初始的无穷大,表示无法在不超过k的限制下将这些单词放在一行内。当所有单词的长度和刚好等于k时,从dp的计算逻辑中可以看出,由于整体长度等于k,dp[i]会被设置为0,表示无额外成本,所有单词恰好放在一行。

相关问题