-2

On input we have a long list of words. And I should return an arbitrary string composed of words present in the input list and only of those. The overall length of the resulting string should be as close to 15 characters(ignoring spaces) from the lower bound (<=15) as possible. As I understood this task is connected to knapsack problem.

For example:

Input:

 'This is a long string that has some words'

Output:

'This is a long that'

My function (but I have compilation error on elif statement):

def return_words(string):
    MAXSYMB = 15
    if not string or string.isspace():
        return 'You did not enter any string'
    str_ign_space = string.replace(' ', '') 
    elif len(str_ign_space) <= MAXSYMB: 
        cur_str = string.split()
        return ' '.join(word for word in cur_str)
    else:
        a = [(i, len(i)) for i in cur_st]
        sorted_items = sorted(((length, word)
                       for word, length in a),
                      reverse = True)
        wt = 0
        bagged = []
        for length, word in sorted_items:
            portion = min(MAXSYMB - wt, length)
            wt     += portion
            bagged += [(word, portion)]
            if wt >= MAXSYMB:
                break
        return ' '.join(item[0] for item in bagged)
lvc
  • 34,233
  • 10
  • 73
  • 98
  • 4
    You're missing a parenthesis on line 8. Voting to close as typo. – anon582847382 Apr 17 '14 at 12:40
  • [this question](http://stackoverflow.com/questions/23131830/how-can-we-remove-all-distinct-words-of-length-16-letters-or-more-using-python) is similar, and may be useful. (classmate of yours? :-) ) – Kevin Apr 17 '14 at 12:46
  • 2
    @AlexThornton He said he had a compilation error on the elif statement. I dont think the missing ) completely solves the issues – heinst Apr 17 '14 at 12:58

3 Answers3

2

Your line

str_ign_space = string.replace(' ', '') 

breaks up your if...elif...else conditional block. You can't start a conditional block with elif, so you're getting a SyntaxError.

Replace your elif with a regular if to start a new conditional block and your code will parse.

dabhaid
  • 3,849
  • 22
  • 30
  • 1
    +1 for actually solving the issue and not just jumping right to the missing ) like the commenters – heinst Apr 17 '14 at 13:00
1

An elif has to direct follow an if or another elif. You are trying to put this statement:

str_ign_space = string.replace(' ', '') 

in between the if and the elif, which doesn't make sense. Instead, put that line before the if. This also lets you simplify your if condition - since you remove all spaces, anytime string.isspace() would be true you would also have str_ign_space being empty:

def return_words(string):
    MAXSYMB = 15
    str_ign_space = string.replace(' ', '')
    if not str_ign_space:
        # Didn't enter a string without spaces
    elif len(str_ign_space) <= MAXSYMB:
        ....

You also have a problem here:

a = [(i, len(i)) for i in cur_st]

this line is directly under the else:, but cur_st is only defined in the elif above it. Whenever the else runs, the elif won't have done (by definition) and cur_st will NameError. I think you probably mean for i in string.split(). And the whole elif block is quite strange. First, note that:

' '.join(word for word in cur_str)

is just the same as:

' '.join(cur_str)

and it becomes apparent that you're splitting the string on whitespace.. only to immediately rejoin those parts with spaces. This is reasonable sometimes (it collapses multiple spaces down to one), but it is rather unusual - and if you're doing it deliberately, it deserves a comment to explain why.

lvc
  • 34,233
  • 10
  • 73
  • 98
0
from collections import Counter

def find_highest_combo_lte_target(lencounts, target):
    # highest achievable values == sum of all items
    total = sum(length*num for length,num in lencounts)

    if total <= target:
        # exact solution or not reachable - return everything
        return lencounts
    else:
        # dynamic programming solution
        found = {0: []}
        for length,num in lencounts:
            new_found = {}
            for k in range(1, num+1):
                val = length * k
                if (target - val) in found:
                    return found[target - val] + [(length, k)]
                else:
                    for total,values in found.items():
                        newtotal = val + total
                        if newtotal < target and newtotal not in found:
                            new_found[newtotal] = found[total] + [(length, k)]
            found.update(new_found)
        best = max(found)
        return found[best]

def build_string(words, lencounts):
    len_num = dict(lencounts)
    result = []
    for word in words:
        wl = len(word)
        if len_num.get(wl, 0) > 0:
            result.append(word)
            len_num[wl] -= 1
    return " ".join(result)

def return_words(s, targetlen):
    words   = s.split()         
    counts  = Counter(len(word) for word in words).items()
    bestsol = find_highest_combo_lte_target(counts, targetlen)
    return build_string(words, bestsol)

def main():
    s = "This is a very long string containing some words odd and eerie"
    for i in range(30):
        print("{:>2}: {}".format(i, return_words(s, i)))

if __name__=="__main__":
    main()

produces

 0: 
 1: a
 2: is
 3: is a
 4: a odd
 5: is odd
 6: is a odd
 7: a odd and
 8: is odd and
 9: is a odd and
10: This odd and
11: This a odd and
12: This is odd and
13: This is a odd and
14: This very odd and
15: This a very odd and
16: This is very odd and
17: This is a very odd and
18: This very long odd and
19: This a very long odd and
20: This is very long odd and
21: This is a very long odd and
22: This very long some odd and
23: This a very long some odd and
24: This is very long some odd and
25: This is a very long some odd and
26: This is very long some words odd
27: This very long some words odd and
28: This a very long some words odd and
29: This is very long some words odd and
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99