迭代遍历哈希表,求解《828. 统计子串中的唯一字符》

例题

828. 统计子串中的唯一字符

我们定义了一个函数 countUniqueChars(s) 来统计字符串 s 中的唯一字符,并返回唯一字符的个数。
例如:s = "LEETCODE" ,则其中 "L", "T","C","O","D" 都是唯一字符,因为它们只出现一次,所以 countUniqueChars(s) = 5 。
本题将会给你一个字符串 s ,我们需要返回 countUniqueChars(t) 的总和,其中 t 是 s 的子字符串。输入用例保证返回值为 32 位整数。
注意,某些子字符串可能是重复的,但你统计时也必须算上这些重复的子字符串(也就是说,你必须统计 s 的所有子字符串中的唯一字符)。
示例 1:
输入: s = "ABC"
输出: 10
解释: 所有可能的子串为:"A","B","C","AB","BC" 和 "ABC"。
其中,每一个子串都由独特字符构成。
所以其长度总和为:1 + 1 + 1 + 2 + 2 + 3 = 10
示例 2:
输入: s = "ABA"
输出: 8
解释: 除了 countUniqueChars("ABA") = 1 之外,其余与示例 1 相同。
示例 3:
输入:s = "LEETCODE"
输出:92
提示:
1 <= s.length <= 10^5
s 只包含大写英文字符

答案

哈希表 · 迭代哈希表

统计每个字符的贡献度:

  1. 存字符出现位置,任意 3 位置的 2 区间:
    • 字符左侧区间:以该字符为最右侧的字串个数 left
    • 字符右侧区间:以该字符为最左侧的字串个数 rgiht
  2. left * right:包含该字符串的子串区间

var uniqueLetterString = function(s) {
  const n = s.length, posMap = new Map
  for (let i = 0; i < n; i++) {
    if (posMap.has(s[i])) posMap.get(s[i]).push(i)
    else posMap.set(s[i], [-1, i])
  }
  let r = 0
  posMap.forEach(pos => {
    const m = pos.length
    pos.push(n)
    for (let i = 1; i < m; i++) {
      r += (pos[i] - pos[i - 1]) * (pos[i + 1] - pos[i]) 
    }
  })
  return r
};
function uniqueLetterString(s: string): number {
  const n = s.length, posMap = new Map
  for (let i = 0; i < n; i++) {
    if (posMap.has(s[i])) posMap.get(s[i]).push(i)
    else posMap.set(s[i], [-1, i])
  }
  let r = 0
  for (const pos of posMap.values()) {
    const m = pos.length
    pos.push(n)
    for (let i = 1; i < m; i++) {
      r += (pos[i] - pos[i - 1]) * (pos[i + 1] - pos[i])
    }
  }
  return r
};
class Solution {
  function uniqueLetterString($s) {
    $n = strlen($s);
    $posMap = array();
    for ($i = 0; $i < $n; $i++) {
      if ($posMap[$s[$i]] === null) $posMap[$s[$i]] = [-1, $i];
      else $posMap[$s[$i]] []= $i;
    }
    $r = 0;
    foreach ($posMap as $pos) {
      $m = count($pos);
      $pos []= $n;
      for ($i = 1; $i < $m; $i++) {
        $r += ($pos[$i] - $pos[$i - 1]) * ($pos[$i + 1] - $pos[$i]);
      }
    }
    return $r;
  }
}
func uniqueLetterString(s string) int {
  n, posMap, r := len(s), map[rune][]int{}, 0
  for i, c := range s {
    if _, ok := posMap[c]; ok {
      posMap[c] = append(posMap[c], i)
    } else {
      posMap[c] = []int{-1, i}
    }
  }
  for _, pos := range posMap {
    m := len(pos)
    pos = append(pos, n)
    for i := 1; i < m; i++ {
      r += (pos[i] - pos[i - 1]) * (pos[i + 1] - pos[i])
    }
  }
  return r
}
func uniqueLetterString(s string) int {
  n, posMap, r := len(s), map[rune][]int{}, 0
  for i, c := range s {
    posMap[c] = append(posMap[c], i)
  }
  for _, pos := range posMap {
    m := len(pos) + 1
    pos = append(append([]int{-1}, pos...), n)
    for i := 1; i < m; i++ {
      r += (pos[i] - pos[i - 1]) * (pos[i + 1] - pos[i])
    }
  }
  return r
}
class Solution {
  public int uniqueLetterString(String s) {
    int n = s.length(), r = 0;
    Map<Character, List<Integer>> posMap = new HashMap<Character, List<Integer>>();
    for (int i = 0; i < n; i++) {
      if (posMap.containsKey(s.charAt(i))) posMap.get(s.charAt(i)).add(i);
      else {
        List<Integer> t = new ArrayList<Integer>();
        t.add(-1);
        t.add(i);
        posMap.put(s.charAt(i), t);
      }
    }
    for (List<Integer> pos : posMap.values()) {
      int m = pos.size();
      pos.add(n);
      for (int i = 1; i < m; i++) {
        r += (pos.get(i) - pos.get(i - 1)) * (pos.get(i + 1) - pos.get(i));
      }
    }
    return r;
  }
}
public class Solution {
  public int UniqueLetterString(string s) {
    int n = s.Length, r = 0;
    Dictionary<int, IList<int>> posMap = new Dictionary<int, IList<int>>();
    for (int i = 0; i < n; i++) {
      if (posMap.ContainsKey(s[i])) posMap[s[i]].Add(i);
      else {
        IList<int> t = new List<int>();
        t.Add(-1);
        t.Add(i);
        posMap.Add(s[i], t);
      }
    }
    foreach (KeyValuePair<int, IList<int>> pair in posMap) {
      IList<int> pos = pair.Value;
      int m = pos.Count;
      pos.Add(n);
      for (int i = 1; i < m; i++) {
        r += (pos[i] - pos[i - 1]) * (pos[i + 1] - pos[i]);
      }
    }
    return r;
  }
}
typedef struct {
  int key;
  int* val;
  int i;
  UT_hash_handle hh;
} HashItem;
int uniqueLetterString(char * s){
  int n = strlen(s), r = 0;
  HashItem* posMap = NULL;
  for (int i = 0; i < n; i++) {
    HashItem* pEntry = NULL;
    int key = s[i] - '0';
    HASH_FIND_INT(posMap, &key, pEntry);
    if (pEntry == NULL) {
      pEntry = malloc(sizeof(HashItem));
      pEntry->key = s[i] - '0';
      int* val = malloc(sizeof(int) * 1e5);
      val[0] = -1;
      val[1] = i;
      pEntry->val = val;
      pEntry->i = 2;
      HASH_ADD_INT(posMap, key, pEntry);
    } else {
      (pEntry->val)[(pEntry->i)++] = i;
    }
  }
  HashItem* tmp, *cur;
  HASH_ITER(hh, posMap, cur, tmp) {
    int m = cur->i;
    int* pos = cur->val;
    pos[m] = n;
    for (int i = 1; i < m; i++) {
      r += (pos[i] - pos[i - 1]) * (pos[i + 1] - pos[i]);
    }
  }
  return r;
}
class Solution {
public:
  int uniqueLetterString(string s) {
    int n = s.size(), r = 0;
    unordered_map<char, vector<int>> posMap;
    for (int i = 0; i < n; i++) {
      if (posMap.find(s[i]) == posMap.end()) posMap.emplace(s[i], vector<int>{-1, i});
      else posMap[s[i]].emplace_back(i);
    }
    for (auto &[_, pos] : posMap) {
      int m = pos.size();
      pos.emplace_back(n);
      for (int i = 1; i < m; i++) {
        r += (pos[i] - pos[i - 1]) * (pos[i + 1] - pos[i]);
      }
    }
    return r;
  }
};
class Solution:
  def uniqueLetterString(self, s: str) -> int:
    n, posMap, r = len(s), dict(), 0
    for i, c in enumerate(s):
      if c not in posMap: posMap[c] = [-1, i]
      else: posMap[c].append(i)
    for pos in posMap.values():
      m = len(pos)
      pos.append(n)
      for i in range(1, m):
        r += (pos[i] - pos[i - 1]) * (pos[i + 1] - pos[i])
    return r

RabinKarp 哈希算法 + 位图(位集)
var uniqueLetterString = function(s) {
  const n = s.length, MOD = 1e8 + 9, BASE = 26, hash = new Uint32Array(n), base = new Uint32Array(n)
  base[0] = 1, hash[0] = s.charCodeAt(0) - 65
  for (let i = 1; i < n; i++) {
    base[i] = base[i - 1] * BASE % MOD
    hash[i] = (hash[i - 1] * BASE + s.charCodeAt(i) - 65) % MOD
  }
  let r = 0
  for (let len = 1; len <= n; len++) {
    const m = new Map()
    for (let i = 0; i + len <= n; i++) {
      const h = (hash[i + len - 1] + MOD - (i > 0 ? hash[i - 1] * base[len] : 0) % MOD) % MOD
      if (m.has(h) === false) m.set(h, countUniqueChars(s, i, i + len))
      r += m.get(h)
    }
  }
  return r 
};
const countUniqueChars = (s, start, end) => {
  const b = new Bit, r = new Bit
  for (let i = start; i < end; i++) {
    const j = s.charCodeAt(i) - 65
    if (b.has(j) === false) {
      b.add(j)
      r.add(j)
    } else {
      r.delete(j)
    }
  }
  return r.size()
}
class Bit {
  constructor() {
    this.bit = 0
  }
  add(i) {
    this.bit |= 1 << i
  }
  has(i) {
    return (this.bit & 1 << i) > 0
  }
  delete(i) {
    if (this.has(i)) this.bit ^= 1 << i
  }
  size() {
    let r = 0, n = this.bit
    while (n) {
      r++
      n &= n - 1
    }
    return r
  }
}
计数排序(API / 哈希映射 / 定长列表实现)+ 自定义排序,3 解法求解《1636. 按照频率将数组升序排序》
计数排序(API / 哈希映射 / 定长列表实现)+ 自定义排序,3 解法求解《1636. 按照频率将数组升序排序》
定长列表,哈希映射两种数据结构,求解《1624. 两个相同字符之间的最长子字符串》
顺序遍历,定长列表,哈希映射两种数据结构,求解《1624. 两个相同字符之间的最长子字符串》
递归,迭代(定长列表 + 位集),3 解法求解《672. 灯泡开关 Ⅱ》
递归,迭代(定长列表 + 位集),3 解法求解《672. 灯泡开关 Ⅱ》