0271-Encode-and-Decode-Strings

Problem Description

Design an algorithm to encode a list of strings to a single string. The encoded string is then decoded back to the original list of strings.

Please implement encode and decode

Solution

核心觀念:把多個字串接成一條字串,難點在 decode 要能切回來。不能只靠分隔符——字串內容本身可能剛好含有該分隔符而造成誤判。正解是 length-prefix(長度前綴):每段前面記下它的長度,decode 時先讀長度、再精確切出那麼多個字元,內容含什麼字元都無所謂。

["abc", "de"]  →  "3#abc2#de"
                   └┬┘ └┬┘
              讀到 # 前=長度 3,之後取 3 字元 "abc";再讀 2,取 "de"

方法一:長度前綴 + 分隔符(通用)— O(n)/O(n)

格式 長度#內容。decode 掃到 # 前的數字是長度 len# 之後 len 個字元就是該段內容。# 只負責分隔「長度數字」與「內容」,因為切割靠的是長度而非搜尋分隔符,內容裡即使出現 # 或數字也不會誤判。任意長度皆可。

// Time: O(n)  n 為所有字元總數
// Space: O(n)
string encode(vector<string> &strs) {
  string out;
  for (auto &s : strs) {
    out += to_string(s.size());
    out += '#';
    out += s;
  }
  return out;
}

vector<string> decode(string s) {
  vector<string> res;
  int i = 0;
  while (i < s.size()) {
    int j = i;
    while (s[j] != '#') {
      ++j;  // 找長度數字的結尾
    }
    int len = stoi(s.substr(i, j - i));
    res.push_back(s.substr(j + 1, len));
    i = j + 1 + len;
  }
  return res;
}
Important

用 length-prefix 而非「純分隔符切割」的理由:字串內容可以是任意字元序列(含 #、逗號、數字)。只有「先讀長度、再照長度精確切」才能保證無歧義還原。

方法二:固定寬度長度前綴 — O(n)/O(n)

每段長度固定用 3 位數表示(setw(3) 補零,前提是單段長度 ≤ 999),decode 每次固定讀 3 位。省掉分隔符、解析更簡單,代價是受長度上限限制。

// Time: O(n)
// Space: O(n)
string encode(vector<string> &strs) {
  stringstream ss;
  for (auto s : strs) {
    ss << setw(3) << setfill('0') << to_string(s.size());
    ss << s;
  }
  return ss.str();
}

vector<string> decode(string s) {
  vector<string> ans;
  int i = 0;
  while (i < s.size()) {
    int len = stoi(s.substr(i, 3));
    ans.push_back(s.substr(i + 3, len));
    i += 3 + len;
  }
  return ans;
}
Warning

固定 3 位版假設每段長度 ≤ 999,超過就會截斷長度、解碼錯位;通用版 長度#內容 沒有這個上限,較穩健。