From 3a9d62b8d869fe374b6b3d8561539433afc63e2e Mon Sep 17 00:00:00 2001 From: yanglbme Date: Thu, 7 Dec 2023 20:37:05 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.2955 No.2955.Number of Same-End Substrings --- .../README.md | 151 +++++++++++++++++- .../README_EN.md | 151 +++++++++++++++++- .../Solution.cpp | 24 +++ .../Solution.go | 26 +++ .../Solution.java | 23 +++ .../Solution.py | 17 ++ .../Solution.rs | 24 +++ .../Solution.ts | 19 +++ 8 files changed, 429 insertions(+), 6 deletions(-) create mode 100644 solution/2900-2999/2955.Number of Same-End Substrings/Solution.cpp create mode 100644 solution/2900-2999/2955.Number of Same-End Substrings/Solution.go create mode 100644 solution/2900-2999/2955.Number of Same-End Substrings/Solution.java create mode 100644 solution/2900-2999/2955.Number of Same-End Substrings/Solution.py create mode 100644 solution/2900-2999/2955.Number of Same-End Substrings/Solution.rs create mode 100644 solution/2900-2999/2955.Number of Same-End Substrings/Solution.ts diff --git a/solution/2900-2999/2955.Number of Same-End Substrings/README.md b/solution/2900-2999/2955.Number of Same-End Substrings/README.md index 7323583b26ab6..496b426e71567 100644 --- a/solution/2900-2999/2955.Number of Same-End Substrings/README.md +++ b/solution/2900-2999/2955.Number of Same-End Substrings/README.md @@ -51,6 +51,12 @@ +**方法一:前缀和 + 枚举** + +我们可以预处理出每个字母的前缀和,记录在数组 $cnt$ 中,其中 $cnt[i][j]$ 表示第 $i$ 个字母在前 $j$ 个字符中出现的次数。这样,对于每个区间 $[l, r]$,我们可以枚举区间中的每个字母 $c$,利用前缀和数组快速计算出 $c$ 在区间中出现的次数 $x$,我们任取其中两个,即可组成一个同尾子串,子串数为 $C_x^2=\frac{x(x-1)}{2}$,加上区间中每个字母可以单独组成同尾子串的情况,一共有 $r - l + 1$ 个字母。因此,对于每个查询 $[l, r]$,满足条件的同尾子串数为 $r - l + 1 + \sum_{c \in \Sigma} \frac{x_c(x_c-1)}{2}$,其中 $x_c$ 表示字母 $c$ 在区间 $[l, r]$ 中出现的次数。 + +时间复杂度 $O((n + m) \times |\Sigma|)$,空间复杂度 $O(n \times |\Sigma|)$。其中 $n$ 和 $m$ 分别为字符串 $s$ 的长度和查询数,而 $\Sigma$ 表示字符串 $s$ 中出现的字母集合,本题中 $|\Sigma|=26$。 + ### **Python3** @@ -58,7 +64,23 @@ ```python - +class Solution: + def sameEndSubstringCount(self, s: str, queries: List[List[int]]) -> List[int]: + n = len(s) + cs = set(s) + cnt = {c: [0] * (n + 1) for c in cs} + for i, a in enumerate(s, 1): + for c in cs: + cnt[c][i] = cnt[c][i - 1] + cnt[a][i] += 1 + ans = [] + for l, r in queries: + t = r - l + 1 + for c in cs: + x = cnt[c][r + 1] - cnt[c][l] + t += x * (x - 1) // 2 + ans.append(t) + return ans ``` ### **Java** @@ -66,19 +88,142 @@ ```java - +class Solution { + public int[] sameEndSubstringCount(String s, int[][] queries) { + int n = s.length(); + int[][] cnt = new int[26][n + 1]; + for (int j = 1; j <= n; ++j) { + for (int i = 0; i < 26; ++i) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s.charAt(j - 1) - 'a'][j]++; + } + int m = queries.length; + int[] ans = new int[m]; + for (int k = 0; k < m; ++k) { + int l = queries[k][0], r = queries[k][1]; + ans[k] = r - l + 1; + for (int i = 0; i < 26; ++i) { + int x = cnt[i][r + 1] - cnt[i][l]; + ans[k] += x * (x - 1) / 2; + } + } + return ans; + } +} ``` ### **C++** ```cpp - +class Solution { +public: + vector sameEndSubstringCount(string s, vector>& queries) { + int n = s.size(); + int cnt[26][n + 1]; + memset(cnt, 0, sizeof(cnt)); + for (int j = 1; j <= n; ++j) { + for (int i = 0; i < 26; ++i) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s[j - 1] - 'a'][j]++; + } + vector ans; + for (auto& q : queries) { + int l = q[0], r = q[1]; + ans.push_back(r - l + 1); + for (int i = 0; i < 26; ++i) { + int x = cnt[i][r + 1] - cnt[i][l]; + ans.back() += x * (x - 1) / 2; + } + } + return ans; + } +}; ``` ### **Go** ```go +func sameEndSubstringCount(s string, queries [][]int) []int { + n := len(s) + cnt := make([][]int, 26) + for i := 0; i < 26; i++ { + cnt[i] = make([]int, n+1) + } + + for j := 1; j <= n; j++ { + for i := 0; i < 26; i++ { + cnt[i][j] = cnt[i][j-1] + } + cnt[s[j-1]-'a'][j]++ + } + + var ans []int + for _, q := range queries { + l, r := q[0], q[1] + ans = append(ans, r-l+1) + for i := 0; i < 26; i++ { + x := cnt[i][r+1] - cnt[i][l] + ans[len(ans)-1] += x * (x - 1) / 2 + } + } + + return ans +} +``` + +### **TypeScript** + +```ts +function sameEndSubstringCount(s: string, queries: number[][]): number[] { + const n: number = s.length; + const cnt: number[][] = Array.from({ length: 26 }, () => Array(n + 1).fill(0)); + for (let j = 1; j <= n; j++) { + for (let i = 0; i < 26; i++) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s.charCodeAt(j - 1) - 'a'.charCodeAt(0)][j]++; + } + const ans: number[] = []; + for (const [l, r] of queries) { + ans.push(r - l + 1); + for (let i = 0; i < 26; i++) { + const x: number = cnt[i][r + 1] - cnt[i][l]; + ans[ans.length - 1] += (x * (x - 1)) / 2; + } + } + return ans; +} +``` +### **Rust** + +```rust +impl Solution { + pub fn same_end_substring_count(s: String, queries: Vec>) -> Vec { + let n = s.len(); + let mut cnt: Vec> = vec![vec![0; n + 1]; 26]; + for j in 1..=n { + for i in 0..26 { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[(s.as_bytes()[j - 1] as usize) - (b'a' as usize)][j] += 1; + } + let mut ans: Vec = Vec::new(); + for q in queries.iter() { + let l = q[0] as usize; + let r = q[1] as usize; + let mut t = (r - l + 1) as i32; + for i in 0..26 { + let x = cnt[i][r + 1] - cnt[i][l]; + t += (x * (x - 1)) / 2; + } + ans.push(t); + } + ans + } +} ``` ### **...** diff --git a/solution/2900-2999/2955.Number of Same-End Substrings/README_EN.md b/solution/2900-2999/2955.Number of Same-End Substrings/README_EN.md index 814c19816557d..58229eecf50ec 100644 --- a/solution/2900-2999/2955.Number of Same-End Substrings/README_EN.md +++ b/solution/2900-2999/2955.Number of Same-End Substrings/README_EN.md @@ -47,30 +47,175 @@ ## Solutions +**Solution 1: Prefix Sum + Enumeration** + +We can preprocess the prefix sum for each letter and record it in the array $cnt$, where $cnt[i][j]$ represents the number of times the $i$-th letter appears in the first $j$ characters. In this way, for each interval $[l, r]$, we can enumerate each letter $c$ in the interval, quickly calculate the number of times $c$ appears in the interval $x$ using the prefix sum array. We can arbitrarily choose two of them to form a tail-equal substring, the number of substrings is $C_x^2=\frac{x(x-1)}{2}$, plus the situation where each letter in the interval can form a tail-equal substring alone, there are $r - l + 1$ letters in total. Therefore, for each query $[l, r]$, the number of tail-equal substrings that meet the conditions is $r - l + 1 + \sum_{c \in \Sigma} \frac{x_c(x_c-1)}{2}$, where $x_c$ represents the number of times the letter $c$ appears in the interval $[l, r]$. + +The time complexity is $O((n + m) \times |\Sigma|)$, and the space complexity is $O(n \times |\Sigma|)$. Here, $n$ and $m$ are the lengths of the string $s$ and the number of queries, respectively, and $\Sigma$ represents the set of letters appearing in the string $s$, in this problem $|\Sigma|=26$. + ### **Python3** ```python - +class Solution: + def sameEndSubstringCount(self, s: str, queries: List[List[int]]) -> List[int]: + n = len(s) + cs = set(s) + cnt = {c: [0] * (n + 1) for c in cs} + for i, a in enumerate(s, 1): + for c in cs: + cnt[c][i] = cnt[c][i - 1] + cnt[a][i] += 1 + ans = [] + for l, r in queries: + t = r - l + 1 + for c in cs: + x = cnt[c][r + 1] - cnt[c][l] + t += x * (x - 1) // 2 + ans.append(t) + return ans ``` ### **Java** ```java - +class Solution { + public int[] sameEndSubstringCount(String s, int[][] queries) { + int n = s.length(); + int[][] cnt = new int[26][n + 1]; + for (int j = 1; j <= n; ++j) { + for (int i = 0; i < 26; ++i) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s.charAt(j - 1) - 'a'][j]++; + } + int m = queries.length; + int[] ans = new int[m]; + for (int k = 0; k < m; ++k) { + int l = queries[k][0], r = queries[k][1]; + ans[k] = r - l + 1; + for (int i = 0; i < 26; ++i) { + int x = cnt[i][r + 1] - cnt[i][l]; + ans[k] += x * (x - 1) / 2; + } + } + return ans; + } +} ``` ### **C++** ```cpp - +class Solution { +public: + vector sameEndSubstringCount(string s, vector>& queries) { + int n = s.size(); + int cnt[26][n + 1]; + memset(cnt, 0, sizeof(cnt)); + for (int j = 1; j <= n; ++j) { + for (int i = 0; i < 26; ++i) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s[j - 1] - 'a'][j]++; + } + vector ans; + for (auto& q : queries) { + int l = q[0], r = q[1]; + ans.push_back(r - l + 1); + for (int i = 0; i < 26; ++i) { + int x = cnt[i][r + 1] - cnt[i][l]; + ans.back() += x * (x - 1) / 2; + } + } + return ans; + } +}; ``` ### **Go** ```go +func sameEndSubstringCount(s string, queries [][]int) []int { + n := len(s) + cnt := make([][]int, 26) + for i := 0; i < 26; i++ { + cnt[i] = make([]int, n+1) + } + + for j := 1; j <= n; j++ { + for i := 0; i < 26; i++ { + cnt[i][j] = cnt[i][j-1] + } + cnt[s[j-1]-'a'][j]++ + } + + var ans []int + for _, q := range queries { + l, r := q[0], q[1] + ans = append(ans, r-l+1) + for i := 0; i < 26; i++ { + x := cnt[i][r+1] - cnt[i][l] + ans[len(ans)-1] += x * (x - 1) / 2 + } + } + + return ans +} +``` + +### **TypeScript** + +```ts +function sameEndSubstringCount(s: string, queries: number[][]): number[] { + const n: number = s.length; + const cnt: number[][] = Array.from({ length: 26 }, () => Array(n + 1).fill(0)); + for (let j = 1; j <= n; j++) { + for (let i = 0; i < 26; i++) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s.charCodeAt(j - 1) - 'a'.charCodeAt(0)][j]++; + } + const ans: number[] = []; + for (const [l, r] of queries) { + ans.push(r - l + 1); + for (let i = 0; i < 26; i++) { + const x: number = cnt[i][r + 1] - cnt[i][l]; + ans[ans.length - 1] += (x * (x - 1)) / 2; + } + } + return ans; +} +``` +### **Rust** + +```rust +impl Solution { + pub fn same_end_substring_count(s: String, queries: Vec>) -> Vec { + let n = s.len(); + let mut cnt: Vec> = vec![vec![0; n + 1]; 26]; + for j in 1..=n { + for i in 0..26 { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[(s.as_bytes()[j - 1] as usize) - (b'a' as usize)][j] += 1; + } + let mut ans: Vec = Vec::new(); + for q in queries.iter() { + let l = q[0] as usize; + let r = q[1] as usize; + let mut t = (r - l + 1) as i32; + for i in 0..26 { + let x = cnt[i][r + 1] - cnt[i][l]; + t += (x * (x - 1)) / 2; + } + ans.push(t); + } + ans + } +} ``` ### **...** diff --git a/solution/2900-2999/2955.Number of Same-End Substrings/Solution.cpp b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.cpp new file mode 100644 index 0000000000000..fe031797bc4c3 --- /dev/null +++ b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.cpp @@ -0,0 +1,24 @@ +class Solution { +public: + vector sameEndSubstringCount(string s, vector>& queries) { + int n = s.size(); + int cnt[26][n + 1]; + memset(cnt, 0, sizeof(cnt)); + for (int j = 1; j <= n; ++j) { + for (int i = 0; i < 26; ++i) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s[j - 1] - 'a'][j]++; + } + vector ans; + for (auto& q : queries) { + int l = q[0], r = q[1]; + ans.push_back(r - l + 1); + for (int i = 0; i < 26; ++i) { + int x = cnt[i][r + 1] - cnt[i][l]; + ans.back() += x * (x - 1) / 2; + } + } + return ans; + } +}; \ No newline at end of file diff --git a/solution/2900-2999/2955.Number of Same-End Substrings/Solution.go b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.go new file mode 100644 index 0000000000000..707c6758f0f14 --- /dev/null +++ b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.go @@ -0,0 +1,26 @@ +func sameEndSubstringCount(s string, queries [][]int) []int { + n := len(s) + cnt := make([][]int, 26) + for i := 0; i < 26; i++ { + cnt[i] = make([]int, n+1) + } + + for j := 1; j <= n; j++ { + for i := 0; i < 26; i++ { + cnt[i][j] = cnt[i][j-1] + } + cnt[s[j-1]-'a'][j]++ + } + + var ans []int + for _, q := range queries { + l, r := q[0], q[1] + ans = append(ans, r-l+1) + for i := 0; i < 26; i++ { + x := cnt[i][r+1] - cnt[i][l] + ans[len(ans)-1] += x * (x - 1) / 2 + } + } + + return ans +} \ No newline at end of file diff --git a/solution/2900-2999/2955.Number of Same-End Substrings/Solution.java b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.java new file mode 100644 index 0000000000000..de53839c7348c --- /dev/null +++ b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.java @@ -0,0 +1,23 @@ +class Solution { + public int[] sameEndSubstringCount(String s, int[][] queries) { + int n = s.length(); + int[][] cnt = new int[26][n + 1]; + for (int j = 1; j <= n; ++j) { + for (int i = 0; i < 26; ++i) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s.charAt(j - 1) - 'a'][j]++; + } + int m = queries.length; + int[] ans = new int[m]; + for (int k = 0; k < m; ++k) { + int l = queries[k][0], r = queries[k][1]; + ans[k] = r - l + 1; + for (int i = 0; i < 26; ++i) { + int x = cnt[i][r + 1] - cnt[i][l]; + ans[k] += x * (x - 1) / 2; + } + } + return ans; + } +} \ No newline at end of file diff --git a/solution/2900-2999/2955.Number of Same-End Substrings/Solution.py b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.py new file mode 100644 index 0000000000000..aa3062a6a91e1 --- /dev/null +++ b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.py @@ -0,0 +1,17 @@ +class Solution: + def sameEndSubstringCount(self, s: str, queries: List[List[int]]) -> List[int]: + n = len(s) + cs = set(s) + cnt = {c: [0] * (n + 1) for c in cs} + for i, a in enumerate(s, 1): + for c in cs: + cnt[c][i] = cnt[c][i - 1] + cnt[a][i] += 1 + ans = [] + for l, r in queries: + t = r - l + 1 + for c in cs: + x = cnt[c][r + 1] - cnt[c][l] + t += x * (x - 1) // 2 + ans.append(t) + return ans diff --git a/solution/2900-2999/2955.Number of Same-End Substrings/Solution.rs b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.rs new file mode 100644 index 0000000000000..ab5c1c073ed12 --- /dev/null +++ b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.rs @@ -0,0 +1,24 @@ +impl Solution { + pub fn same_end_substring_count(s: String, queries: Vec>) -> Vec { + let n = s.len(); + let mut cnt: Vec> = vec![vec![0; n + 1]; 26]; + for j in 1..=n { + for i in 0..26 { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[(s.as_bytes()[j - 1] as usize) - (b'a' as usize)][j] += 1; + } + let mut ans: Vec = Vec::new(); + for q in queries.iter() { + let l = q[0] as usize; + let r = q[1] as usize; + let mut t = (r - l + 1) as i32; + for i in 0..26 { + let x = cnt[i][r + 1] - cnt[i][l]; + t += (x * (x - 1)) / 2; + } + ans.push(t); + } + ans + } +} diff --git a/solution/2900-2999/2955.Number of Same-End Substrings/Solution.ts b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.ts new file mode 100644 index 0000000000000..a77b394ce7114 --- /dev/null +++ b/solution/2900-2999/2955.Number of Same-End Substrings/Solution.ts @@ -0,0 +1,19 @@ +function sameEndSubstringCount(s: string, queries: number[][]): number[] { + const n: number = s.length; + const cnt: number[][] = Array.from({ length: 26 }, () => Array(n + 1).fill(0)); + for (let j = 1; j <= n; j++) { + for (let i = 0; i < 26; i++) { + cnt[i][j] = cnt[i][j - 1]; + } + cnt[s.charCodeAt(j - 1) - 'a'.charCodeAt(0)][j]++; + } + const ans: number[] = []; + for (const [l, r] of queries) { + ans.push(r - l + 1); + for (let i = 0; i < 26; i++) { + const x: number = cnt[i][r + 1] - cnt[i][l]; + ans[ans.length - 1] += (x * (x - 1)) / 2; + } + } + return ans; +}