Skip to content

Commit 6e71522

Browse files
authored
feat: add solutions to lc problem: No.2955 (#2070)
No.2955.Number of Same-End Substrings
1 parent d6e1fb1 commit 6e71522

File tree

8 files changed

+429
-6
lines changed

8 files changed

+429
-6
lines changed

solution/2900-2999/2955.Number of Same-End Substrings/README.md

Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,34 +51,179 @@
5151

5252
<!-- 这里可写通用的实现逻辑 -->
5353

54+
**方法一:前缀和 + 枚举**
55+
56+
我们可以预处理出每个字母的前缀和,记录在数组 $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]$ 中出现的次数。
57+
58+
时间复杂度 $O((n + m) \times |\Sigma|)$,空间复杂度 $O(n \times |\Sigma|)$。其中 $n$ 和 $m$ 分别为字符串 $s$ 的长度和查询数,而 $\Sigma$ 表示字符串 $s$ 中出现的字母集合,本题中 $|\Sigma|=26$。
59+
5460
<!-- tabs:start -->
5561

5662
### **Python3**
5763

5864
<!-- 这里可写当前语言的特殊实现逻辑 -->
5965

6066
```python
61-
67+
class Solution:
68+
def sameEndSubstringCount(self, s: str, queries: List[List[int]]) -> List[int]:
69+
n = len(s)
70+
cs = set(s)
71+
cnt = {c: [0] * (n + 1) for c in cs}
72+
for i, a in enumerate(s, 1):
73+
for c in cs:
74+
cnt[c][i] = cnt[c][i - 1]
75+
cnt[a][i] += 1
76+
ans = []
77+
for l, r in queries:
78+
t = r - l + 1
79+
for c in cs:
80+
x = cnt[c][r + 1] - cnt[c][l]
81+
t += x * (x - 1) // 2
82+
ans.append(t)
83+
return ans
6284
```
6385

6486
### **Java**
6587

6688
<!-- 这里可写当前语言的特殊实现逻辑 -->
6789

6890
```java
69-
91+
class Solution {
92+
public int[] sameEndSubstringCount(String s, int[][] queries) {
93+
int n = s.length();
94+
int[][] cnt = new int[26][n + 1];
95+
for (int j = 1; j <= n; ++j) {
96+
for (int i = 0; i < 26; ++i) {
97+
cnt[i][j] = cnt[i][j - 1];
98+
}
99+
cnt[s.charAt(j - 1) - 'a'][j]++;
100+
}
101+
int m = queries.length;
102+
int[] ans = new int[m];
103+
for (int k = 0; k < m; ++k) {
104+
int l = queries[k][0], r = queries[k][1];
105+
ans[k] = r - l + 1;
106+
for (int i = 0; i < 26; ++i) {
107+
int x = cnt[i][r + 1] - cnt[i][l];
108+
ans[k] += x * (x - 1) / 2;
109+
}
110+
}
111+
return ans;
112+
}
113+
}
70114
```
71115

72116
### **C++**
73117

74118
```cpp
75-
119+
class Solution {
120+
public:
121+
vector<int> sameEndSubstringCount(string s, vector<vector<int>>& queries) {
122+
int n = s.size();
123+
int cnt[26][n + 1];
124+
memset(cnt, 0, sizeof(cnt));
125+
for (int j = 1; j <= n; ++j) {
126+
for (int i = 0; i < 26; ++i) {
127+
cnt[i][j] = cnt[i][j - 1];
128+
}
129+
cnt[s[j - 1] - 'a'][j]++;
130+
}
131+
vector<int> ans;
132+
for (auto& q : queries) {
133+
int l = q[0], r = q[1];
134+
ans.push_back(r - l + 1);
135+
for (int i = 0; i < 26; ++i) {
136+
int x = cnt[i][r + 1] - cnt[i][l];
137+
ans.back() += x * (x - 1) / 2;
138+
}
139+
}
140+
return ans;
141+
}
142+
};
76143
```
77144
78145
### **Go**
79146
80147
```go
148+
func sameEndSubstringCount(s string, queries [][]int) []int {
149+
n := len(s)
150+
cnt := make([][]int, 26)
151+
for i := 0; i < 26; i++ {
152+
cnt[i] = make([]int, n+1)
153+
}
154+
155+
for j := 1; j <= n; j++ {
156+
for i := 0; i < 26; i++ {
157+
cnt[i][j] = cnt[i][j-1]
158+
}
159+
cnt[s[j-1]-'a'][j]++
160+
}
161+
162+
var ans []int
163+
for _, q := range queries {
164+
l, r := q[0], q[1]
165+
ans = append(ans, r-l+1)
166+
for i := 0; i < 26; i++ {
167+
x := cnt[i][r+1] - cnt[i][l]
168+
ans[len(ans)-1] += x * (x - 1) / 2
169+
}
170+
}
171+
172+
return ans
173+
}
174+
```
175+
176+
### **TypeScript**
177+
178+
```ts
179+
function sameEndSubstringCount(s: string, queries: number[][]): number[] {
180+
const n: number = s.length;
181+
const cnt: number[][] = Array.from({ length: 26 }, () => Array(n + 1).fill(0));
182+
for (let j = 1; j <= n; j++) {
183+
for (let i = 0; i < 26; i++) {
184+
cnt[i][j] = cnt[i][j - 1];
185+
}
186+
cnt[s.charCodeAt(j - 1) - 'a'.charCodeAt(0)][j]++;
187+
}
188+
const ans: number[] = [];
189+
for (const [l, r] of queries) {
190+
ans.push(r - l + 1);
191+
for (let i = 0; i < 26; i++) {
192+
const x: number = cnt[i][r + 1] - cnt[i][l];
193+
ans[ans.length - 1] += (x * (x - 1)) / 2;
194+
}
195+
}
196+
return ans;
197+
}
198+
```
81199

200+
### **Rust**
201+
202+
```rust
203+
impl Solution {
204+
pub fn same_end_substring_count(s: String, queries: Vec<Vec<i32>>) -> Vec<i32> {
205+
let n = s.len();
206+
let mut cnt: Vec<Vec<i32>> = vec![vec![0; n + 1]; 26];
207+
for j in 1..=n {
208+
for i in 0..26 {
209+
cnt[i][j] = cnt[i][j - 1];
210+
}
211+
cnt[(s.as_bytes()[j - 1] as usize) - (b'a' as usize)][j] += 1;
212+
}
213+
let mut ans: Vec<i32> = Vec::new();
214+
for q in queries.iter() {
215+
let l = q[0] as usize;
216+
let r = q[1] as usize;
217+
let mut t = (r - l + 1) as i32;
218+
for i in 0..26 {
219+
let x = cnt[i][r + 1] - cnt[i][l];
220+
t += (x * (x - 1)) / 2;
221+
}
222+
ans.push(t);
223+
}
224+
ans
225+
}
226+
}
82227
```
83228

84229
### **...**

solution/2900-2999/2955.Number of Same-End Substrings/README_EN.md

Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,30 +47,175 @@
4747

4848
## Solutions
4949

50+
**Solution 1: Prefix Sum + Enumeration**
51+
52+
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]$.
53+
54+
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$.
55+
5056
<!-- tabs:start -->
5157

5258
### **Python3**
5359

5460
```python
55-
61+
class Solution:
62+
def sameEndSubstringCount(self, s: str, queries: List[List[int]]) -> List[int]:
63+
n = len(s)
64+
cs = set(s)
65+
cnt = {c: [0] * (n + 1) for c in cs}
66+
for i, a in enumerate(s, 1):
67+
for c in cs:
68+
cnt[c][i] = cnt[c][i - 1]
69+
cnt[a][i] += 1
70+
ans = []
71+
for l, r in queries:
72+
t = r - l + 1
73+
for c in cs:
74+
x = cnt[c][r + 1] - cnt[c][l]
75+
t += x * (x - 1) // 2
76+
ans.append(t)
77+
return ans
5678
```
5779

5880
### **Java**
5981

6082
```java
61-
83+
class Solution {
84+
public int[] sameEndSubstringCount(String s, int[][] queries) {
85+
int n = s.length();
86+
int[][] cnt = new int[26][n + 1];
87+
for (int j = 1; j <= n; ++j) {
88+
for (int i = 0; i < 26; ++i) {
89+
cnt[i][j] = cnt[i][j - 1];
90+
}
91+
cnt[s.charAt(j - 1) - 'a'][j]++;
92+
}
93+
int m = queries.length;
94+
int[] ans = new int[m];
95+
for (int k = 0; k < m; ++k) {
96+
int l = queries[k][0], r = queries[k][1];
97+
ans[k] = r - l + 1;
98+
for (int i = 0; i < 26; ++i) {
99+
int x = cnt[i][r + 1] - cnt[i][l];
100+
ans[k] += x * (x - 1) / 2;
101+
}
102+
}
103+
return ans;
104+
}
105+
}
62106
```
63107

64108
### **C++**
65109

66110
```cpp
67-
111+
class Solution {
112+
public:
113+
vector<int> sameEndSubstringCount(string s, vector<vector<int>>& queries) {
114+
int n = s.size();
115+
int cnt[26][n + 1];
116+
memset(cnt, 0, sizeof(cnt));
117+
for (int j = 1; j <= n; ++j) {
118+
for (int i = 0; i < 26; ++i) {
119+
cnt[i][j] = cnt[i][j - 1];
120+
}
121+
cnt[s[j - 1] - 'a'][j]++;
122+
}
123+
vector<int> ans;
124+
for (auto& q : queries) {
125+
int l = q[0], r = q[1];
126+
ans.push_back(r - l + 1);
127+
for (int i = 0; i < 26; ++i) {
128+
int x = cnt[i][r + 1] - cnt[i][l];
129+
ans.back() += x * (x - 1) / 2;
130+
}
131+
}
132+
return ans;
133+
}
134+
};
68135
```
69136
70137
### **Go**
71138
72139
```go
140+
func sameEndSubstringCount(s string, queries [][]int) []int {
141+
n := len(s)
142+
cnt := make([][]int, 26)
143+
for i := 0; i < 26; i++ {
144+
cnt[i] = make([]int, n+1)
145+
}
146+
147+
for j := 1; j <= n; j++ {
148+
for i := 0; i < 26; i++ {
149+
cnt[i][j] = cnt[i][j-1]
150+
}
151+
cnt[s[j-1]-'a'][j]++
152+
}
153+
154+
var ans []int
155+
for _, q := range queries {
156+
l, r := q[0], q[1]
157+
ans = append(ans, r-l+1)
158+
for i := 0; i < 26; i++ {
159+
x := cnt[i][r+1] - cnt[i][l]
160+
ans[len(ans)-1] += x * (x - 1) / 2
161+
}
162+
}
163+
164+
return ans
165+
}
166+
```
167+
168+
### **TypeScript**
169+
170+
```ts
171+
function sameEndSubstringCount(s: string, queries: number[][]): number[] {
172+
const n: number = s.length;
173+
const cnt: number[][] = Array.from({ length: 26 }, () => Array(n + 1).fill(0));
174+
for (let j = 1; j <= n; j++) {
175+
for (let i = 0; i < 26; i++) {
176+
cnt[i][j] = cnt[i][j - 1];
177+
}
178+
cnt[s.charCodeAt(j - 1) - 'a'.charCodeAt(0)][j]++;
179+
}
180+
const ans: number[] = [];
181+
for (const [l, r] of queries) {
182+
ans.push(r - l + 1);
183+
for (let i = 0; i < 26; i++) {
184+
const x: number = cnt[i][r + 1] - cnt[i][l];
185+
ans[ans.length - 1] += (x * (x - 1)) / 2;
186+
}
187+
}
188+
return ans;
189+
}
190+
```
73191

192+
### **Rust**
193+
194+
```rust
195+
impl Solution {
196+
pub fn same_end_substring_count(s: String, queries: Vec<Vec<i32>>) -> Vec<i32> {
197+
let n = s.len();
198+
let mut cnt: Vec<Vec<i32>> = vec![vec![0; n + 1]; 26];
199+
for j in 1..=n {
200+
for i in 0..26 {
201+
cnt[i][j] = cnt[i][j - 1];
202+
}
203+
cnt[(s.as_bytes()[j - 1] as usize) - (b'a' as usize)][j] += 1;
204+
}
205+
let mut ans: Vec<i32> = Vec::new();
206+
for q in queries.iter() {
207+
let l = q[0] as usize;
208+
let r = q[1] as usize;
209+
let mut t = (r - l + 1) as i32;
210+
for i in 0..26 {
211+
let x = cnt[i][r + 1] - cnt[i][l];
212+
t += (x * (x - 1)) / 2;
213+
}
214+
ans.push(t);
215+
}
216+
ans
217+
}
218+
}
74219
```
75220

76221
### **...**
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class Solution {
2+
public:
3+
vector<int> sameEndSubstringCount(string s, vector<vector<int>>& queries) {
4+
int n = s.size();
5+
int cnt[26][n + 1];
6+
memset(cnt, 0, sizeof(cnt));
7+
for (int j = 1; j <= n; ++j) {
8+
for (int i = 0; i < 26; ++i) {
9+
cnt[i][j] = cnt[i][j - 1];
10+
}
11+
cnt[s[j - 1] - 'a'][j]++;
12+
}
13+
vector<int> ans;
14+
for (auto& q : queries) {
15+
int l = q[0], r = q[1];
16+
ans.push_back(r - l + 1);
17+
for (int i = 0; i < 26; ++i) {
18+
int x = cnt[i][r + 1] - cnt[i][l];
19+
ans.back() += x * (x - 1) / 2;
20+
}
21+
}
22+
return ans;
23+
}
24+
};

0 commit comments

Comments
 (0)