|
| 1 | +from typing import List |
| 2 | + |
| 3 | +class TextJustifier: |
| 4 | + def __init__(self, words: List[str], max_width: int): |
| 5 | + self.words = words |
| 6 | + self.max_width = max_width |
| 7 | + self.n = len(words) |
| 8 | + self.dp = [float('inf')] * (self.n + 1) |
| 9 | + self.dp[self.n] = 0 |
| 10 | + self.breaks = [-1] * (self.n + 1) |
| 11 | + |
| 12 | + def _cost(self, i: int, j: int) -> float: |
| 13 | + length = sum(len(self.words[k]) for k in range(i, j + 1)) + (j - i) |
| 14 | + if length > self.max_width: |
| 15 | + return float('inf') |
| 16 | + return (self.max_width - length) ** 3 |
| 17 | + |
| 18 | + def justify(self) -> List[str]: |
| 19 | + for i in range(self.n - 1, -1, -1): |
| 20 | + for j in range(i, self.n): |
| 21 | + cost = self._cost(i, j) |
| 22 | + if cost == float('inf'): |
| 23 | + break |
| 24 | + if self.dp[j + 1] + cost < self.dp[i]: |
| 25 | + self.dp[i] = self.dp[j + 1] + cost |
| 26 | + self.breaks[i] = j |
| 27 | + |
| 28 | + lines = [] |
| 29 | + i = 0 |
| 30 | + while i < self.n: |
| 31 | + j = self.breaks[i] |
| 32 | + line_words = self.words[i:j + 1] |
| 33 | + line = self._justify_line(line_words) |
| 34 | + lines.append(line) |
| 35 | + i = j + 1 |
| 36 | + return lines |
| 37 | + |
| 38 | + def _justify_line(self, line_words: List[str]) -> str: |
| 39 | + if len(line_words) == 1: |
| 40 | + return line_words[0] + ' ' * (self.max_width - len(line_words[0])) |
| 41 | + |
| 42 | + total_spaces = self.max_width - sum(len(word) for word in line_words) |
| 43 | + spaces_between_words = len(line_words) - 1 |
| 44 | + space, extra = divmod(total_spaces, spaces_between_words) |
| 45 | + |
| 46 | + line = '' |
| 47 | + for i, word in enumerate(line_words[:-1]): |
| 48 | + line += word + ' ' * (space + (1 if i < extra else 0)) |
| 49 | + line += line_words[-1] |
| 50 | + return line |
0 commit comments