简体   繁体   中英

Decode all possibilities from a given encoded ascii string

A string is encoded by performing the following sequence of actions:
1. Replace each character with its ASCII value representation.
2. Reverse the string.

For example, the table below shows the conversion from the string "HelloWorld" to the ASCII string "7210110810811187111114108100" :

Character

H  e   l   l   o    W  o   r   l   d

ASCII Value

72 101 108 108 111  87 111 114 108 100

The ASCII string is then reversed to get the encoded string "0018014111117811180180110127 ".

The characters in encoded string are within the range 10 - 126 which include special characters.

I need to write a function that must decode the encoded string and return the list of ways in which it can be decoded.

I could not get a work around to solve this. Any help is appreciated.

solutions = []
currentSolution = ''
unprocessed = ''


def decode(s):
    flipped = s[::-1]
    global solutions
    global unprocessed
    global currentSolution
    currentSolution = ''
    unprocessed = flipped
    _decode()
    return solutions


def is_valid(split):
    if split.startswith('0'):
        return False
    value = int(split)
    if value < 10 or value > 126:
        return False
    return True


def _decode():
    global unprocessed
    global currentSolution
    global solutions
    if len(unprocessed) == 0:
        solutions.append(currentSolution)
    else:
        possible_splits = list()
        possible_splits.append(unprocessed[0:2])
        possible_splits.append(unprocessed[0:3])

        for split in possible_splits:
            if is_valid(split):
                decoded_character = chr(int(split))
                currentSolution += decoded_character
                unprocessed = unprocessed[len(split):]
                _decode()
                currentSolution = currentSolution[0: len(currentSolution) - 1]
                unprocessed = split + unprocessed


def main():
    final_solutions = decode('0018014111117811180180110127')
    print(final_solutions)


if __name__ == '__main__':
    main()

I'd like to point out that you're asking a very computer science question--one I'd see in an intro data structures/algorithms class. I'm assuming you know some of the terminology I'll be using here given that context.


I used backtracking to calculate every possible split and if the split was valid (eg used all numbers), then I added it to a list of solutions.

You'll notice that it isn't the most efficient algorithm but it still works on your input. I believe the time complexity is something like O(2^n) (though I may be wrong about that) because, @Mulliganaceous pointed out, at each decision to split, you create another branch which has at most two options.

Furthermore, since you want all possible ways, you need to exhaust this search tree until you find all solutions. You might be able to get a faster algorithm using dynamic programming but I haven't thought about this problem enough to do so.

So here's the O(2^n) solution:

The code below has these steps in the comments.

  1. initial some state. you need:
    1. an empty list of solutions
    2. the current solution being considered
    3. the current unprocessed substring
  2. kick off the backtracking with an initial decode function/method. in this method, flip the input and assign that to the unprocessed substring. You can additionally reset the other state parameters if you plan on running this function more than once. call the recursive _decode function to kick off the backtracking.
  3. in the backtracking method, check to see if a solution is found (base case when the unprocessed string is empty). if it has been found, add the current solution to the list of solutions
  4. after the solution check, generate a list of all possible splits for the current unprocessed string. For example: let's say the unprocessed string is 123456 . For this string, there are only 2 possible splits for this branch: 12 and 123 . If the unprocessed string was just 78 , there would only be one possible split: 78 .
  5. iterate through the list of possible splits for the current branch of the implicit search tree. Check to see if the split is valid. In this case, your requirements said the split is valid if the parsed string as a number is between 10 and 126 inclusive. Additionally, the string with the split cannot start with zero because when the string is originally made, there is no possibility for padded zeros.
  6. if the split is valid, then change the state. In this case, add the decoded character to the solution and remove the split from the unprocessed string (because you processed it)
  7. go deeper. recursively call the _decode method. this will keep going down the search tree until it doesn't have any options.
  8. backtrack. once the callstack comes back up from this call, it means that it didn't find any options. you have to undo the state you set above it. in this case, remove the character from the current solution and add the split back to the unprocessed string.

And that's that!

But here's some javascript code (the only reason it's in javascript is so you can press the "Run code snippet" button in browser).

If you're on mac, hit Cmd + option I and then click "Run code snippet" If you're on windows, his F12 and then click "Run code snippet"

You'll see your browser open up a debugger. Step through the code . See what it's doing.

 // 1 let solutions = []; let currentSolution = ''; let unprocessed = ''; function range(n) { const list = []; for (let i = 0; i < n; i += 1) { list.push(i); } return list; } // 2 function decode(input) { const flipped = input .split('') .reverse() .join(''); solutions = []; currentSolution = ''; unprocessed = flipped; _decode(); return solutions.slice(); } function isValid(split) { if (split.startsWith('0')) return false; const value = parseInt(split, 10); if (value < 10) return false; if (value > 126) return false; return true; } function _decode() { // 3 if (unprocessed.length === 0) { solutions.push(currentSolution); } // 4 const possibleSplits = range(2) .map(i => i + 1) .filter(i => i < unprocessed.length) .map(i => unprocessed.substring(0, i + 1)); // 5 for (const split of possibleSplits) { if (isValid(split)) { const decodedCharacter = String.fromCharCode(parseInt(split, 10)); // 6 currentSolution += decodedCharacter; unprocessed = unprocessed.substring(split.length); // 7 _decode(); // 8 currentSolution = currentSolution.substring(0, currentSolution.length - 1); unprocessed = split + unprocessed; } } } debugger; console.time('calculation time'); const finalSolutions = decode('0018014111117811180180110127'); console.timeEnd('calculation time'); console.log(finalSolutions.length, 'solutions (all but one of them contain special characters)'); console.log(finalSolutions); console.log('final solutions in ascii code'); console.log(finalSolutions .map(solution => solution .split('') .map(c => c.charCodeAt(0)) .map(code => code.toString()) .join(', ') ) .map(line => `[${line}]`) .join('\\n') ) 

Can you also add an additional part to your code which prints out every char as an ASCII code, each encased in brackets? – Mulliganaceous 24 mins ago

sure thing, see the edited answer.

Edit: updated the isValid function to check to see if the string starts with 0 , if it does, then it should not consider that split valid.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM