给定一个单词序列,以及一行中可以放置的字符数(行宽)的限制。在给定的序列中放置换行符,以便整齐地打印行。假设每个字的长度小于线宽。
像 MS Word 这样的文字处理器可以完成放置换行符的任务。这个想法是有平衡的线条。换句话说,不是有大量额外空格的几行和一些带有少量额外空格的行。
The extra spaces includes spaces put at the end of every line except the last one.
The problem is to minimize the following total cost.
Cost of a line = (Number of extra spaces in the line)^3
Total Cost = Sum of costs for all lines
For example, consider the following string and line width M = 15
"Geeks for Geeks presents word wrap problem"
Following is the optimized arrangement of words in 3 lines
Geeks for Geeks
presents word
wrap problem
The total extra spaces in line 1, line 2 and line 3 are 0, 2 and 3 respectively.
So optimal value of total cost is 0 + 2*2 + 3*3 = 13
请注意,总成本函数不是额外空间的总和,而是额外空间的立方(或平方)的总和。这个成本函数背后的想法是平衡线之间的空间。例如,考虑以下两个相同词组的排列:
1)有 3 行。一行有 3 个额外的空格,所有其他行有 0 个额外的空格。总额外空间 = 3 + 0 + 0 = 3。总成本 = 3*3*3 + 0*0*0 + 0*0*0 = 27。
2)有 3 行。 3 行中的每一行都有一个额外的空间。总额外空间 = 1 + 1 + 1 = 3。总成本 = 1*1*1 + 1*1*1 + 1*1*1 = 3。
在这两种情况下,额外空格总数均为 3,但应首选第二种排列,因为所有三行中的额外空格都是平衡的。具有三次总和的成本函数可以达到目的,因为第二种情况下的总成本值较小。
方法一(贪心解)
贪婪的解决方案是将尽可能多的单词放在第一行。然后对第二行做同样的事情,依此类推,直到放置所有单词。该解在很多情况下都给出了最优解,但并不是在所有情况下都给出了最优解。例如,考虑以下字符串“aaa bb cc ddddd”和线宽为 6。贪婪方法将产生以下输出。
aaa bb
cc
ddddd
以上3行中多余的空格分别为0、4、1。所以总成本是 0 + 64 + 1 = 65。
但上述解决方案并不是最好的解决方案。下面的排列有更平衡的空间。因此总成本函数的值较小。
aaa
bb cc
ddddd
以上3行中多余的空格分别为3、1、1。所以总成本是 27 + 1 + 1 = 29。
尽管在某些情况下是次优的,但许多文字处理器(如 MS Word 和 OpenOffice.org Writer)都使用贪婪方法。
方法二(动态规划)
以下动态方法严格遵循 Cormen 书的解决方案中给出的算法。首先,我们计算二维表 lc[][] 中所有可能行的成本。值 lc[i][j] 表示将 i 到 j 的单词放在一行中的成本,其中 i 和 j 是输入序列中单词的索引。如果从 i 到 j 的单词序列不能放在一行中,则 lc[i][j] 被认为是无限的(以避免它成为解决方案的一部分)。一旦我们构建了 lc[][] 表,我们就可以使用以下递归公式计算总成本。在下面的公式中,C[j] 是从 1 到 j 排列词的优化总成本。
上述递归具有重叠子问题的性质。例如,子问题 c(2) 的解被 c(3)、C(4) 等使用。所以动态规划用于存储子问题的结果。数组 c[] 可以从左到右计算,因为每个值只依赖于较早的值。
为了打印输出,我们跟踪哪些单词出现在哪些行上,我们可以保留一个平行的 p 数组,指向每个 c 值的来源。最后一行从单词 p[n] 开始,经过单词 n。前一行从单词 p[p[n]] 开始,经过单词 p[n ] – 1 等。函数printSolution() 使用 p[] 打印解决方案。
在下面的程序中,输入是一个数组 l[],表示序列中单词的长度。值 l[i] 表示输入序列中第 i 个单词(i 从 1 开始)的长度。
C++
// A Dynamic programming solution for Word Wrap Problem
#include
using namespace std;
#define INF INT_MAX
// A utility function to print the solution
int printSolution (int p[], int n);
// l[] represents lengths of different words in input sequence.
// For example, l[] = {3, 2, 2, 5} is for a sentence like
// "aaa bb cc ddddd". n is size of l[] and M is line width
// (maximum no. of characters that can fit in a line)
void solveWordWrap (int l[], int n, int M)
{
// For simplicity, 1 extra space is used in all below arrays
// extras[i][j] will have number of extra spaces if words from i
// to j are put in a single line
int extras[n+1][n+1];
// lc[i][j] will have cost of a line which has words from
// i to j
int lc[n+1][n+1];
// c[i] will have total cost of optimal arrangement of words
// from 1 to i
int c[n+1];
// p[] is used to print the solution.
int p[n+1];
int i, j;
// calculate extra spaces in a single line. The value extra[i][j]
// indicates extra spaces if words from word number i to j are
// placed in a single line
for (i = 1; i <= n; i++)
{
extras[i][i] = M - l[i-1];
for (j = i+1; j <= n; j++)
extras[i][j] = extras[i][j-1] - l[j-1] - 1;
}
// Calculate line cost corresponding to the above calculated extra
// spaces. The value lc[i][j] indicates cost of putting words from
// word number i to j in a single line
for (i = 1; i <= n; i++)
{
for (j = i; j <= n; j++)
{
if (extras[i][j] < 0)
lc[i][j] = INF;
else if (j == n && extras[i][j] >= 0)
lc[i][j] = 0;
else
lc[i][j] = extras[i][j]*extras[i][j];
}
}
// Calculate minimum cost and find minimum cost arrangement.
// The value c[j] indicates optimized cost to arrange words
// from word number 1 to j.
c[0] = 0;
for (j = 1; j <= n; j++)
{
c[j] = INF;
for (i = 1; i <= j; i++)
{
if (c[i-1] != INF && lc[i][j] != INF &&
(c[i-1] + lc[i][j] < c[j]))
{
c[j] = c[i-1] + lc[i][j];
p[j] = i;
}
}
}
printSolution(p, n);
}
int printSolution (int p[], int n)
{
int k;
if (p[n] == 1)
k = 1;
else
k = printSolution (p, p[n]-1) + 1;
cout<<"Line number "<
C
// A Dynamic programming solution for Word Wrap Problem
#include
#include
#define INF INT_MAX
// A utility function to print the solution
int printSolution (int p[], int n);
// l[] represents lengths of different words in input sequence.
// For example, l[] = {3, 2, 2, 5} is for a sentence like
// "aaa bb cc ddddd". n is size of l[] and M is line width
// (maximum no. of characters that can fit in a line)
void solveWordWrap (int l[], int n, int M)
{
// For simplicity, 1 extra space is used in all below arrays
// extras[i][j] will have number of extra spaces if words from i
// to j are put in a single line
int extras[n+1][n+1];
// lc[i][j] will have cost of a line which has words from
// i to j
int lc[n+1][n+1];
// c[i] will have total cost of optimal arrangement of words
// from 1 to i
int c[n+1];
// p[] is used to print the solution.
int p[n+1];
int i, j;
// calculate extra spaces in a single line. The value extra[i][j]
// indicates extra spaces if words from word number i to j are
// placed in a single line
for (i = 1; i <= n; i++)
{
extras[i][i] = M - l[i-1];
for (j = i+1; j <= n; j++)
extras[i][j] = extras[i][j-1] - l[j-1] - 1;
}
// Calculate line cost corresponding to the above calculated extra
// spaces. The value lc[i][j] indicates cost of putting words from
// word number i to j in a single line
for (i = 1; i <= n; i++)
{
for (j = i; j <= n; j++)
{
if (extras[i][j] < 0)
lc[i][j] = INF;
else if (j == n && extras[i][j] >= 0)
lc[i][j] = 0;
else
lc[i][j] = extras[i][j]*extras[i][j];
}
}
// Calculate minimum cost and find minimum cost arrangement.
// The value c[j] indicates optimized cost to arrange words
// from word number 1 to j.
c[0] = 0;
for (j = 1; j <= n; j++)
{
c[j] = INF;
for (i = 1; i <= j; i++)
{
if (c[i-1] != INF && lc[i][j] != INF &&
(c[i-1] + lc[i][j] < c[j]))
{
c[j] = c[i-1] + lc[i][j];
p[j] = i;
}
}
}
printSolution(p, n);
}
int printSolution (int p[], int n)
{
int k;
if (p[n] == 1)
k = 1;
else
k = printSolution (p, p[n]-1) + 1;
printf ("Line number %d: From word no. %d to %d \n", k, p[n], n);
return k;
}
// Driver program to test above functions
int main()
{
int l[] = {3, 2, 2, 5};
int n = sizeof(l)/sizeof(l[0]);
int M = 6;
solveWordWrap (l, n, M);
return 0;
}
Java
// A Dynamic programming solution for
// Word Wrap Problem in Java
public class WordWrap
{
final int MAX = Integer.MAX_VALUE;
// A utility function to print the solution
int printSolution (int p[], int n)
{
int k;
if (p[n] == 1)
k = 1;
else
k = printSolution (p, p[n]-1) + 1;
System.out.println("Line number" + " " + k + ": " +
"From word no." +" "+ p[n] + " " + "to" + " " + n);
return k;
}
// l[] represents lengths of different words in input sequence.
// For example, l[] = {3, 2, 2, 5} is for a sentence like
// "aaa bb cc ddddd". n is size of l[] and M is line width
// (maximum no. of characters that can fit in a line)
void solveWordWrap (int l[], int n, int M)
{
// For simplicity, 1 extra space is used in all below arrays
// extras[i][j] will have number of extra spaces if words from i
// to j are put in a single line
int extras[][] = new int[n+1][n+1];
// lc[i][j] will have cost of a line which has words from
// i to j
int lc[][]= new int[n+1][n+1];
// c[i] will have total cost of optimal arrangement of words
// from 1 to i
int c[] = new int[n+1];
// p[] is used to print the solution.
int p[] =new int[n+1];
// calculate extra spaces in a single line. The value extra[i][j]
// indicates extra spaces if words from word number i to j are
// placed in a single line
for (int i = 1; i <= n; i++)
{
extras[i][i] = M - l[i-1];
for (int j = i+1; j <= n; j++)
extras[i][j] = extras[i][j-1] - l[j-1] - 1;
}
// Calculate line cost corresponding to the above calculated extra
// spaces. The value lc[i][j] indicates cost of putting words from
// word number i to j in a single line
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
if (extras[i][j] < 0)
lc[i][j] = MAX;
else if (j == n && extras[i][j] >= 0)
lc[i][j] = 0;
else
lc[i][j] = extras[i][j]*extras[i][j];
}
}
// Calculate minimum cost and find minimum cost arrangement.
// The value c[j] indicates optimized cost to arrange words
// from word number 1 to j.
c[0] = 0;
for (int j = 1; j <= n; j++)
{
c[j] = MAX;
for (int i = 1; i <= j; i++)
{
if (c[i-1] != MAX && lc[i][j] != MAX &&
(c[i-1] + lc[i][j] < c[j]))
{
c[j] = c[i-1] + lc[i][j];
p[j] = i;
}
}
}
printSolution(p, n);
}
public static void main(String args[])
{
WordWrap w = new WordWrap();
int l[] = {3, 2, 2, 5};
int n = l.length;
int M = 6;
w.solveWordWrap (l, n, M);
}
}
// This code is contributed by Saket Kumar
Python3
# A Dynamic programming solution
# for Word Wrap Problem
# A utility function to print
# the solution
# l[] represents lengths of different
# words in input sequence. For example,
# l[] = {3, 2, 2, 5} is for a sentence
# like "aaa bb cc ddddd". n is size of
# l[] and M is line width (maximum no.
# of characters that can fit in a line)
INF = 2147483647
def printSolution(p, n):
k = 0
if p[n] == 1:
k = 1
else:
k = printSolution(p, p[n] - 1) + 1
print('Line number ', k, ': From word no. ',
p[n], 'to ', n)
return k
def solveWordWrap (l, n, M):
# For simplicity, 1 extra space is
# used in all below arrays
# extras[i][j] will have number
# of extra spaces if words from i
# to j are put in a single line
extras = [[0 for i in range(n + 1)]
for i in range(n + 1)]
# lc[i][j] will have cost of a line
# which has words from i to j
lc = [[0 for i in range(n + 1)]
for i in range(n + 1)]
# c[i] will have total cost of
# optimal arrangement of words
# from 1 to i
c = [0 for i in range(n + 1)]
# p[] is used to print the solution.
p = [0 for i in range(n + 1)]
# calculate extra spaces in a single
# line. The value extra[i][j] indicates
# extra spaces if words from word number
# i to j are placed in a single line
for i in range(n + 1):
extras[i][i] = M - l[i - 1]
for j in range(i + 1, n + 1):
extras[i][j] = (extras[i][j - 1] -
l[j - 1] - 1)
# Calculate line cost corresponding
# to the above calculated extra
# spaces. The value lc[i][j] indicates
# cost of putting words from word number
# i to j in a single line
for i in range(n + 1):
for j in range(i, n + 1):
if extras[i][j] < 0:
lc[i][j] = INF;
elif j == n and extras[i][j] >= 0:
lc[i][j] = 0
else:
lc[i][j] = (extras[i][j] *
extras[i][j])
# Calculate minimum cost and find
# minimum cost arrangement. The value
# c[j] indicates optimized cost to
# arrange words from word number 1 to j.
c[0] = 0
for j in range(1, n + 1):
c[j] = INF
for i in range(1, j + 1):
if (c[i - 1] != INF and
lc[i][j] != INF and
((c[i - 1] + lc[i][j]) < c[j])):
c[j] = c[i-1] + lc[i][j]
p[j] = i
printSolution(p, n)
# Driver Code
l = [3, 2, 2, 5]
n = len(l)
M = 6
solveWordWrap(l, n, M)
# This code is contributed by sahil shelangia
C#
// A Dynamic programming solution for Word Wrap
// Problem in Java
using System;
public class GFG {
static int MAX = int.MaxValue;
// A utility function to print the solution
static int printSolution (int []p, int n)
{
int k;
if (p[n] == 1)
k = 1;
else
k = printSolution (p, p[n]-1) + 1;
Console.WriteLine("Line number" + " "
+ k + ": From word no." + " "
+ p[n] + " " + "to" + " " + n);
return k;
}
// l[] represents lengths of different
// words in input sequence. For example,
// l[] = {3, 2, 2, 5} is for a sentence
// like "aaa bb cc ddddd". n is size of
// l[] and M is line width (maximum no.
// of characters that can fit in a line)
static void solveWordWrap (int []l, int n,
int M)
{
// For simplicity, 1 extra space
// is used in all below arrays
// extras[i][j] will have number of
// extra spaces if words from i
// to j are put in a single line
int [,]extras = new int[n+1,n+1];
// lc[i][j] will have cost of a line
// which has words from i to j
int [,]lc = new int[n+1,n+1];
// c[i] will have total cost of
// optimal arrangement of words
// from 1 to i
int []c = new int[n+1];
// p[] is used to print the solution.
int []p = new int[n+1];
// calculate extra spaces in a single
// line. The value extra[i][j] indicates
// extra spaces if words from word number
// i to j are placed in a single line
for (int i = 1; i <= n; i++)
{
extras[i,i] = M - l[i-1];
for (int j = i+1; j <= n; j++)
extras[i,j] = extras[i,j-1]
- l[j-1] - 1;
}
// Calculate line cost corresponding to
// the above calculated extra spaces. The
// value lc[i][j] indicates cost of
// putting words from word number i to
// j in a single line
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
if (extras[i,j] < 0)
lc[i,j] = MAX;
else if (j == n &&
extras[i,j] >= 0)
lc[i,j] = 0;
else
lc[i,j] = extras[i,j]
* extras[i,j];
}
}
// Calculate minimum cost and find
// minimum cost arrangement. The value
// c[j] indicates optimized cost to
// arrange words from word number
// 1 to j.
c[0] = 0;
for (int j = 1; j <= n; j++)
{
c[j] = MAX;
for (int i = 1; i <= j; i++)
{
if (c[i-1] != MAX && lc[i,j]
!= MAX && (c[i-1] + lc[i,j]
< c[j]))
{
c[j] = c[i-1] + lc[i,j];
p[j] = i;
}
}
}
printSolution(p, n);
}
// Driver code
public static void Main()
{
int []l = {3, 2, 2, 5};
int n = l.Length;
int M = 6;
solveWordWrap (l, n, M);
}
}
// This code is contributed by nitin mittal.
Javascript
输出:
Line number 1: From word no. 1 to 1
Line number 2: From word no. 2 to 3
Line number 3: From word no. 4 to 4
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。