返回介绍

solution / 2900-2999 / 2950.Number of Divisible Substrings / README

发布于 2024-06-17 01:02:58 字数 13240 浏览 0 评论 0 收藏 0

2950. 可整除子串的数量

English Version

题目描述

每个英文字母都被映射到一个数字,如下所示。

如果字符串的字符的映射值的总和可以被字符串的长度整除,则该字符串是 可整除 的。

给定一个字符串 s,请返回 s 的_ 可整除子串 的数量_。

子串 是字符串内的一个连续的非空字符序列。

 

示例 1:

SubstringMappedSumLengthDivisible?
a111Yes
s771Yes
d221Yes
f331Yes
as1, 782Yes
sd7, 292No
df2, 352No
asd1, 7, 2103No
sdf7, 2, 3123Yes
asdf1, 7, 2, 3134No
输入:word = "asdf"
输出:6
解释:上表包含了有关 word 中每个子串的详细信息,我们可以看到其中有 6 个是可整除的。

示例 2:

输入:word = "bdh"
输出:4
解释:4 个可整除的子串是:"b","d","h","bdh"。
可以证明 word 中没有其他可整除的子串。

示例 3:

输入:word = "abcd"
输出:6
解释:6 个可整除的子串是:"a","b","c","d","ab","cd"。
可以证明 word 中没有其他可整除的子串。

 

提示:

  • 1 <= word.length <= 2000
  • word 仅包含小写英文字母。

解法

方法一:枚举

我们先用一个哈希表或数组 $mp$ 记录每个字母对应的数字。

然后,我们枚举子串的起始位置 $i$,再枚举子串的结束位置 $j$,计算子串 $s[i..j]$ 的数字和 $s$,如果 $s$ 能被 $j-i+1$ 整除,那么就找到了一个可被整除的子串,将答案加一。

枚举结束后,返回答案。

时间复杂度 $O(n^2)$,空间复杂度 $O(C)$。其中 $n$ 是字符串 $word$ 的长度,而 $C$ 是字符集的大小,本题中 $C=26$。

class Solution:
  def countDivisibleSubstrings(self, word: str) -> int:
    d = ["ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"]
    mp = {}
    for i, s in enumerate(d, 1):
      for c in s:
        mp[c] = i
    ans = 0
    n = len(word)
    for i in range(n):
      s = 0
      for j in range(i, n):
        s += mp[word[j]]
        ans += s % (j - i + 1) == 0
    return ans
class Solution {
  public int countDivisibleSubstrings(String word) {
    String[] d = {"ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"};
    int[] mp = new int[26];
    for (int i = 0; i < d.length; ++i) {
      for (char c : d[i].toCharArray()) {
        mp[c - 'a'] = i + 1;
      }
    }
    int ans = 0;
    int n = word.length();
    for (int i = 0; i < n; ++i) {
      int s = 0;
      for (int j = i; j < n; ++j) {
        s += mp[word.charAt(j) - 'a'];
        ans += s % (j - i + 1) == 0 ? 1 : 0;
      }
    }
    return ans;
  }
}
class Solution {
public:
  int countDivisibleSubstrings(string word) {
    string d[9] = {"ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"};
    int mp[26]{};
    for (int i = 0; i < 9; ++i) {
      for (char& c : d[i]) {
        mp[c - 'a'] = i + 1;
      }
    }
    int ans = 0;
    int n = word.size();
    for (int i = 0; i < n; ++i) {
      int s = 0;
      for (int j = i; j < n; ++j) {
        s += mp[word[j] - 'a'];
        ans += s % (j - i + 1) == 0 ? 1 : 0;
      }
    }
    return ans;
  }
};
func countDivisibleSubstrings(word string) (ans int) {
  d := []string{"ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"}
  mp := [26]int{}
  for i, s := range d {
    for _, c := range s {
      mp[c-'a'] = i + 1
    }
  }
  n := len(word)
  for i := 0; i < n; i++ {
    s := 0
    for j := i; j < n; j++ {
      s += mp[word[j]-'a']
      if s%(j-i+1) == 0 {
        ans++
      }
    }
  }
  return
}
function countDivisibleSubstrings(word: string): number {
  const d: string[] = ['ab', 'cde', 'fgh', 'ijk', 'lmn', 'opq', 'rst', 'uvw', 'xyz'];
  const mp: number[] = Array(26).fill(0);
  for (let i = 0; i < d.length; ++i) {
    for (const c of d[i]) {
      mp[c.charCodeAt(0) - 'a'.charCodeAt(0)] = i + 1;
    }
  }
  const n = word.length;
  let ans = 0;
  for (let i = 0; i < n; ++i) {
    let s = 0;
    for (let j = i; j < n; ++j) {
      s += mp[word.charCodeAt(j) - 'a'.charCodeAt(0)];
      if (s % (j - i + 1) === 0) {
        ++ans;
      }
    }
  }
  return ans;
}
impl Solution {
  pub fn count_divisible_substrings(word: String) -> i32 {
    let d = vec!["ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"];
    let mut mp = vec![0; 26];

    for (i, s) in d.iter().enumerate() {
      s.chars().for_each(|c| {
        mp[(c as usize) - ('a' as usize)] = (i + 1) as i32;
      });
    }

    let mut ans = 0;
    let n = word.len();

    for i in 0..n {
      let mut s = 0;

      for j in i..n {
        s += mp[(word.as_bytes()[j] as usize) - ('a' as usize)];
        ans += (s % ((j - i + 1) as i32) == 0) as i32;
      }
    }

    ans
  }
}

方法二:哈希表 + 前缀和 + 枚举

与方法一类似,我们先用一个哈希表或数组 $mp$ 记录每个字母对应的数字。

如果一个整数子数组的数字之和能被它的长度整除,那么这个子数组的平均值一定是一个整数。而由于子数组中每个元素的数字都在 $[1, 9]$ 范围内,因此子数组的平均值只能是 $1, 2, \cdots, 9$ 中的一个。

我们可以枚举子数组的平均值 $i$,如果一个子数组的元素和能被 $i$ 整除,假设子数组为 $a_1, a_2, \cdots, a_k$,那么 $a_1 + a_2 + \cdots + a_k = i \times k$,即 $(a_1 - i) + (a_2 - i) + \cdots + (a_k - i) = 0$。如果我们把 $a_k - i$ 视为一个新的元素 $b_k$,那么原来的子数组就变成了 $b_1, b_2, \cdots, b_k$,其中 $b_1 + b_2 + \cdots + b_k = 0$。我们只需要求出新的数组中,有多少个子数组的元素和为 $0$ 即可,这可以用“哈希表”结合“前缀和”来实现。

时间复杂度 $O(10 \times n)$,空间复杂度 $O(n)$。其中 $n$ 是字符串 $word$ 的长度。

class Solution:
  def countDivisibleSubstrings(self, word: str) -> int:
    d = ["ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"]
    mp = {}
    for i, s in enumerate(d, 1):
      for c in s:
        mp[c] = i
    ans = 0
    for i in range(1, 10):
      cnt = defaultdict(int)
      cnt[0] = 1
      s = 0
      for c in word:
        s += mp[c] - i
        ans += cnt[s]
        cnt[s] += 1
    return ans
class Solution {
  public int countDivisibleSubstrings(String word) {
    String[] d = {"ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"};
    int[] mp = new int[26];
    for (int i = 0; i < d.length; ++i) {
      for (char c : d[i].toCharArray()) {
        mp[c - 'a'] = i + 1;
      }
    }
    int ans = 0;
    char[] cs = word.toCharArray();
    for (int i = 1; i < 10; ++i) {
      Map<Integer, Integer> cnt = new HashMap<>();
      cnt.put(0, 1);
      int s = 0;
      for (char c : cs) {
        s += mp[c - 'a'] - i;
        ans += cnt.getOrDefault(s, 0);
        cnt.merge(s, 1, Integer::sum);
      }
    }
    return ans;
  }
}
class Solution {
public:
  int countDivisibleSubstrings(string word) {
    string d[9] = {"ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"};
    int mp[26]{};
    for (int i = 0; i < 9; ++i) {
      for (char& c : d[i]) {
        mp[c - 'a'] = i + 1;
      }
    }
    int ans = 0;
    for (int i = 1; i < 10; ++i) {
      unordered_map<int, int> cnt{{0, 1}};
      int s = 0;
      for (char& c : word) {
        s += mp[c - 'a'] - i;
        ans += cnt[s]++;
      }
    }
    return ans;
  }
};
func countDivisibleSubstrings(word string) (ans int) {
  d := []string{"ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"}
  mp := [26]int{}
  for i, s := range d {
    for _, c := range s {
      mp[c-'a'] = i + 1
    }
  }
  for i := 0; i < 10; i++ {
    cnt := map[int]int{0: 1}
    s := 0
    for _, c := range word {
      s += mp[c-'a'] - i
      ans += cnt[s]
      cnt[s]++
    }
  }
  return
}
function countDivisibleSubstrings(word: string): number {
  const d = ['ab', 'cde', 'fgh', 'ijk', 'lmn', 'opq', 'rst', 'uvw', 'xyz'];
  const mp: number[] = Array(26).fill(0);

  d.forEach((s, i) => {
    for (const c of s) {
      mp[c.charCodeAt(0) - 'a'.charCodeAt(0)] = i + 1;
    }
  });

  let ans = 0;
  for (let i = 0; i < 10; i++) {
    const cnt: { [key: number]: number } = { 0: 1 };
    let s = 0;
    for (const c of word) {
      s += mp[c.charCodeAt(0) - 'a'.charCodeAt(0)] - i;
      ans += cnt[s] || 0;
      cnt[s] = (cnt[s] || 0) + 1;
    }
  }
  return ans;
}
use std::collections::HashMap;

impl Solution {
  pub fn count_divisible_substrings(word: String) -> i32 {
    let d = vec!["ab", "cde", "fgh", "ijk", "lmn", "opq", "rst", "uvw", "xyz"];
    let mut mp: Vec<usize> = vec![0; 26];
    for (i, s) in d.iter().enumerate() {
      for c in s.chars() {
        mp[(c as usize) - ('a' as usize)] = i + 1;
      }
    }
    let mut ans = 0;
    for i in 0..10 {
      let mut cnt: HashMap<i32, i32> = HashMap::new();
      cnt.insert(0, 1);
      let mut s = 0;
      for c in word.chars() {
        s += (mp[(c as usize) - ('a' as usize)] - i) as i32;
        ans += cnt.get(&s).cloned().unwrap_or(0);
        *cnt.entry(s).or_insert(0) += 1;
      }
    }
    ans
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文