150NeetCode 150

8.Encode and Decode Strings

MediumBlind 75Arrays & Hashing ยท 8/9

Problem

Design an algorithm that encodes a list of strings into a single string, and a matching algorithm that decodes that string back into the original list.

The strings may contain any characters โ€” including commas, digits, #, or any delimiter you might be tempted to use โ€” and may be empty. decode(encode(strs)) must return a list equal to strs. No serialization helpers like eval are allowed; the format is yours to design.

Examples

Input:  ["neet", "code", "love", "you"]
Output: ["neet", "code", "love", "you"]
Explanation: encode produces one string (e.g. "4#neet4#code4#love3#you"); decode restores the list.
Input:  ["we", "say", ":", "yes"]
Output: ["we", "say", ":", "yes"]
Input:  ["", "#3", "a,b"]
Output: ["", "#3", "a,b"]
Explanation: Empty strings and strings containing the delimiter must survive the round trip.

Constraints

  • 0 <= strs.length < 100
  • 0 <= strs[i].length < 200
  • strs[i] contains any of the 256 valid ASCII characters.

๐Ÿ’ก Key Insight

A plain delimiter fails because the delimiter itself can appear inside a string. The fix is length-prefixing: write each string as <length>#<string>. The decoder reads digits up to the first #, which gives the exact length, then copies that many characters without looking at them โ€” so a # or digit inside the payload can never be misread as structure.

Escaped Delimiter

TimeO(m)SpaceO(m)

Intuition

Use a separator such as , between strings, and make it unambiguous by escaping: every / in a string becomes // and every , becomes /,. A lone , (not preceded by an escape) is then always a separator. To distinguish an empty list from a list holding one empty string, terminate every string with a , rather than only separating them.

Algorithm

  1. Encode: for each string, append each character (doubling / and prefixing , with /), then append a terminating ,.
  2. Decode: scan characters with a StringBuilder cur:
    • On /, append the next character literally and skip it.
    • On an unescaped ,, finish cur as one string and reset it.
    • Otherwise append the character.

Visualization

strs   = ["a,b", "", "c/"]
encode โ†’ "a/,b,,c//,"

decode segments:
  a / , b ,   โ†’ "/," is an escaped comma, then "," ends โ†’ "a,b"
  ,           โ†’ ends immediately                        โ†’ ""
  c / / ,     โ†’ "//" is an escaped slash, then "," ends โ†’ "c/"

Code

java
import java.util.ArrayList;
import java.util.List;

class Solution {
    public String encode(List<String> strs) {
        StringBuilder sb = new StringBuilder();
        for (String s : strs) {
            for (char ch : s.toCharArray()) {
                if (ch == '/' || ch == ',') {
                    sb.append('/'); // escape the escape char and the separator
                }
                sb.append(ch);
            }
            sb.append(','); // terminator after every string (handles empty strings)
        }
        return sb.toString();
    }

    public List<String> decode(String str) {
        List<String> result = new ArrayList<>();
        StringBuilder cur = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i);
            if (ch == '/') {
                cur.append(str.charAt(i + 1)); // take the escaped char literally
                i++;
            } else if (ch == ',') {
                result.add(cur.toString()); // unescaped comma ends a string
                cur.setLength(0);
            } else {
                cur.append(ch);
            }
        }
        return result;
    }
}

Complexity

  • Time: O(m) โ€” m is the total number of characters; each is processed a constant number of times.
  • Space: O(m) โ€” the encoded string can be up to twice the input size in the worst case (all escapes).

Complexity Comparison

Approach Time Space
Escaped Delimiter O(m) O(m), up to 2ร— expansion
Length Prefix O(m) O(m), small header per string

Common Pitfalls

  • Using a bare delimiter (e.g. joining with , or #) โ€” breaks as soon as a string contains that character.
  • Searching for the # with indexOf('#') from the wrong start index, or scanning inside the payload for structure; the payload of "3#" contains a #.
  • Forgetting empty strings: [""] must encode to something non-empty ("0#"), otherwise it is indistinguishable from [].
  • Assuming the length is a single digit โ€” lengths up to 199 need multi-digit parsing.