Design an algorithm to encode a list of strings to a string. The encoded string is then sent over the network and is decoded back to the original list of strings.
Please implement encode
and decode
Given strs = ["lint","code","love","you"]
string encoded_string = encode(strs)
return `["lint","code","love","you"]` when you call decode(encoded_string)
Solution 1. Application of escape sequence
A straightforward idea is to add ";" between two strings as the divide symbol. But the problem here is,
what about if the input strings contain ";" already? How do we distinguish between the divide symbol ";"
and the ";" that are part of the input strings?
Consider how escape sequence works. represents the start of an escape sequence, like is a newline
character. Since is already reserved as the start of any escape sequence, \ is used to represent the
backslash literal. We can simply borrow this idea and use it in this problem.
Let's define : as the start of escape sequence, so :; is our divide symbol now.
Encode: Replace all : with :: in each string, so :: represents the original : literal;
Connect each string with divide symbol :;
Decode: Scan from left to right until a : is met.
If the next character is ;, it means we have a :; divide symbol;
If not, since we've already replaced all : with :: in encoding, so
we know that we just saw a : literal.
1 public class Solution { 2 public String encode(List<String> strs) { 3 if(strs == null){ 4 return null; 5 } 6 if(strs.size() == 0){ 7 return ":;"; 8 } 9 StringBuffer sb = new StringBuffer(); 10 for(String s : strs){ 11 String newStr = s.replaceAll(":", "::"); 12 sb.append(newStr); 13 sb.append(":;"); 14 } 15 return sb.toString(); 16 } 17 public List<String> decode(String str) { 18 if(str == null){ 19 return null; 20 } 21 List<String> strs = new ArrayList<String>(); 22 int idx = 0; 23 StringBuffer sb = new StringBuffer(); 24 while(idx < str.length()){ 25 if(str.charAt(idx) != ':'){ 26 sb.append(str.charAt(idx)); 27 idx++; 28 } 29 else if(str.charAt(idx + 1) == ';'){ 30 strs.add(sb.toString()); 31 idx += 2; 32 sb = new StringBuffer(); 33 } 34 else{ 35 sb.append(":"); 36 idx += 2; 37 } 38 } 39 return strs; 40 } 41 }
Solution 2. Attach each string's length as a prefix
Encode: add String.valueOf(str.length()) + '$' as a prefix to each string; '$' is the divide symbol between each string's length and
the string itself.
Q: Can we add each string's length as a suffix?
A: No, we can't. If we add as suffix and the string already contains $, we won't be able to get the right length info.
Add as prefix does not have this problem as the very first time we see a $, we know the length info is right prior to this $.
1 public class Solution { 2 /** 3 * @param strs a list of strings 4 * @return encodes a list of strings to a single string. 5 */ 6 public String encode(List<String> strs) { 7 // Write your code here 8 StringBuilder result = new StringBuilder(); 9 for(String str : strs){ 10 result.append(String.valueOf(str.length()) + "$"); 11 result.append(str); 12 } 13 return result.toString(); 14 } 15 16 /** 17 * @param str a string 18 * @return dcodes a single string to a list of strings 19 */ 20 public List<String> decode(String str) { 21 // Write your code here 22 List<String> result = new LinkedList<String>(); 23 int start = 0; 24 while(start < str.length()){ 25 int idx = str.indexOf('$', start); 26 int size = Integer.parseInt(str.substring(start, idx)); 27 result.add(str.substring(idx + 1, idx + size + 1)); 28 start = idx + size + 1; 29 } 30 return result; 31 } 32 }
Runtime: Both solutions have a runtime of O(n * k), n is the number of strings, k is the average number of characters in each string.
Related Problems