[LintCode] Strings Serialization
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 \n 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