Ideas: use two helper functions `Corner`

and `Normal`

to deal with different cases and to make the code clear. These two functions calculate the spaces needed between words (`spaces`

) and then join the words and spaces. It has 22 lines and the best run time is 44ms.

```
def fullJustify(self, words, maxWidth):
def Corner(L):
n = len(L)
spaces = [1 for i in range(n-1)] + [0]
s = "".join(L[i] + " "*spaces[i] for i in range(n))
return s + " "*(maxWidth-len(s))
def Normal(L, tolspace):
space, r = divmod(tolspace, n-1)
spaces = [space+1 if i < r else space for i in range(n-1)] + [0]
return "".join(L[i] + " "*spaces[i] for i in range(n))
ans, curr, temp = [], 0, 0
for i in range(len(words)):
le = len(words[i])
if curr + le <= maxWidth:
curr += le +1
continue
n = i - temp
if n==1:
ans.append(Corner(words[temp:i]))
else:
ans.append(Normal(words[temp:i], maxWidth-curr+n))
temp, curr = i, le + 1
return ans + [Corner(words[temp:])]
```

I also tried to use just one helper function to make it shorter. It has 20 lines -- only 2 lines shorter than the first one but not as clear. (I was able to further shorten it to 18 lines at some expenses of readability, which is not worth it to me)

```
def fullJustify(self, words, maxWidth):
def Combine(L, tolspace=0, space=1, r=0, corner=False):
n=len(L)
if not corner:
space, r = divmod(tolspace, n-1)
spaces = [space+1 if i < r else space for i in range(n-1)] + [0]
s = "".join(L[i] + " "*spaces[i] for i in range(n))
return s + " "*(maxWidth-len(s))
ans, curr, temp = [], 0, 0
for i in range(len(words)):
le = len(words[i])
if curr + le <= maxWidth:
curr += le +1
continue
n = i - temp
if n==1:
ans.append(Combine(words[temp:i], corner=True))
else:
ans.append(Combine(words[temp:i], tolspace=maxWidth-curr+n))
temp, curr = i, le + 1
return ans + [Combine(words[temp:], corner=True)]
```