🍭 大家好这里是KK爱Coding ,一枚热爱算法的程序员
✨ 本系列打算持续跟新美团近期的春秋招笔试题汇总~
💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导
👏 感谢大家的订阅➕ 和 喜欢💗
📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取,会在飞书进行同步的跟新。
文章目录
- 🍓 01.K小姐的好子矩阵
- 问题描述
- 输入格式
- 输出格式
- 样例输入
- 样例输出
- 数据范围
- 题解
- 参考代码
- 🍇 02.卢小姐的魔法数组
- 问题描述
- 输入格式
- 输出格式
- 样例输入
- 样例输出
- 数据范围
- 题解
- 参考代码
- 🥥 03.A先生的红黑树
- 问题描述
- 输入格式
- 输出格式
- 样例输入
- 样例输出
- 数据范围
- 题解
- 参考代码
- 🧀 04.LYA的因子数量查询
- 问题描述
- 输入格式
- 输出格式
- 样例输入
- 样例输出
- 数据范围
- 题解
- 参考代码
- 🍅 05.LYA的字符串子序列集合
- 问题描述
- 输入格式
- 输出格式
- 样例输入
- 样例输出
- 数据范围
- 题解
- 参考代码
- 写在最后
- 📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取,会在飞书进行同步的跟新。
🍓 01.K小姐的好子矩阵
问题描述
K小姐拿到了一个 n n n 行 m m m 列的矩阵,她想知道这个矩阵中有多少个 2 × 2 2 \times 2 2×2 的子矩阵是"好子矩阵"。一个子矩阵是"好子矩阵",当且仅当这个 2 × 2 2 \times 2 2×2 的子矩阵中的所有元素都相同。
输入格式
第一行包含两个正整数 n n n 和 m m m,表示矩阵的行数和列数。
接下来的 n n n 行,每行包含 m m m 个正整数,表示这个矩阵。
输出格式
输出一个整数,表示 2 × 2 2 \times 2 2×2 好子矩阵的数量。
样例输入
3 3
1 2 1
1 1 1
1 1 3
样例输出
1
数据范围
1
≤
n
,
m
≤
100
1 \leq n, m \leq 100
1≤n,m≤100
1
≤
a
i
,
j
≤
1
0
9
1 \leq a_{i,j} \leq 10^9
1≤ai,j≤109
题解
我们可以枚举矩阵中的每个 2 × 2 2 \times 2 2×2 的子矩阵,判断其是否为好子矩阵。具体做法如下:
-
枚举子矩阵的左上角位置 ( i , j ) (i, j) (i,j),其中 1 ≤ i ≤ n − 1 1 \leq i \leq n-1 1≤i≤n−1, 1 ≤ j ≤ m − 1 1 \leq j \leq m-1 1≤j≤m−1。
-
对于每个 ( i , j ) (i, j) (i,j),判断以其为左上角的 2 × 2 2 \times 2 2×2 子矩阵是否为好子矩阵,即判断 a i , j a_{i,j} ai,j, a i , j + 1 a_{i,j+1} ai,j+1, a i + 1 , j a_{i+1,j} ai+1,j 和 a i + 1 , j + 1 a_{i+1,j+1} ai+1,j+1 是否都相等。
-
如果是好子矩阵,将答案加 1 1 1。
-
枚举完所有的 ( i , j ) (i, j) (i,j) 后,即可得到好子矩阵的数量。
时间复杂度 O ( n m ) O(nm) O(nm),空间复杂度 O ( 1 ) O(1) O(1)。
参考代码
- Python
n, m = map(int, input().split())
matrix = [list(map(int, input().split())) for _ in range(n)]
ans = 0
for i in range(n - 1):
for j in range(m - 1):
if matrix[i][j] == matrix[i][j + 1] == matrix[i + 1][j] == matrix[i + 1][j + 1]:
ans += 1
print(ans)
- Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] matrix = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
matrix[i][j] = sc.nextInt();
}
}
int ans = 0;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < m - 1; j++) {
if (matrix[i][j] == matrix[i][j + 1] &&
matrix[i][j] == matrix[i + 1][j] &&
matrix[i][j] == matrix[i + 1][j + 1]) {
ans++;
}
}
}
System.out.println(ans);
}
}
- Cpp
#include <iostream>
using namespace std;
const int N = 110;
int matrix[N][N];
int main() {
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> matrix[i][j];
}
}
int ans = 0;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < m - 1; j++) {
if (matrix[i][j] == matrix[i][j + 1] &&
matrix[i][j] == matrix[i + 1][j] &&
matrix[i][j] == matrix[i + 1][j + 1]) {
ans++;
}
}
}
cout << ans << endl;
return 0;
}
🍇 02.卢小姐的魔法数组
问题描述
卢小姐有一个长度为 n n n 的整数数组。她可以对数组进行最多 k k k 次操作,每次操作可以选择数组中的任意一个元素,将其加 1 1 1 或减 1 1 1。卢小姐希望经过这些操作后,数组中元素为 0 0 0 的数量尽可能多。你能帮助她计算出最多能有多少个元素变成 0 0 0 吗?
输入格式
第一行包含两个正整数 n n n 和 k k k,分别表示数组的长度和最多的操作次数。
第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an,表示初始的数组。
输出格式
输出一个整数,表示经过最多 k k k 次操作后,数组中元素为 0 0 0 的最大数量。
样例输入
4 5
-2 3 -2 9
样例输出
2
数据范围
1
≤
n
≤
1
0
5
1 \leq n \leq 10^5
1≤n≤105
1
≤
k
≤
1
0
14
1 \leq k \leq 10^{14}
1≤k≤1014
−
1
0
9
≤
a
i
≤
1
0
9
-10^9 \leq a_i \leq 10^9
−109≤ai≤109
题解
可以先将数组中的元素全部转换为绝对值,然后对数组进行排序。接着从小到大遍历数组,对于每个元素,如果 k k k 减去当前元素的值大于等于 0 0 0,就将 k k k 减去当前元素的值,并将答案加 1 1 1。如果 k k k 的值已经小于当前元素,说明无法继续转换了,直接退出循环即可。
最终的答案即为数组中可以变成 0 0 0 的元素的最大数量。
时间复杂度为 O ( n log n ) O(n \log n) O(nlogn),空间复杂度为 O ( n ) O(n) O(n)。
参考代码
- Python
n, k = map(int, input().split())
arr = list(map(int, input().split()))
arr = [abs(x) for x in arr]
arr.sort()
count = 0
for x in arr:
if k >= x:
k -= x
count += 1
else:
break
print(count)
- Java
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long k = sc.nextLong();
long[] arr = new long[n];
for (int i = 0; i < n; i++) {
arr[i] = Math.abs(sc.nextLong());
}
Arrays.sort(arr);
int count = 0;
for (long x : arr) {
if (k >= x) {
k -= x;
count++;
} else {
break;
}
}
System.out.println(count);
}
}
- Cpp
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 100010;
long long arr[N];
int main() {
int n;
long long k;
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> arr[i];
arr[i] = abs(arr[i]);
}
sort(arr, arr + n);
int count = 0;
for (int i = 0; i < n; i++) {
if (k >= arr[i]) {
k -= arr[i];
count++;
} else {
break;
}
}
cout << count << endl;
return 0;
}
🥥 03.A先生的红黑树
问题描述
A先生有一棵 n n n 个节点的树,根节点编号为 1 1 1。树上的每个节点都被染成了红色或黑色。A先生想知道,有多少个节点满足以该节点为根的子树中同时包含红色节点和黑色节点。
输入格式
第一行包含一个正整数 n n n,表示树的节点数。
第二行包含一个长度为
n
n
n 的字符串
s
s
s,其中
s
i
s_i
si 表示编号为
i
i
i 的节点的颜色。如果
s
i
s_i
si 为 B
,则表示该节点为黑色;如果
s
i
s_i
si 为 R
,则表示该节点为红色。
接下来 n − 1 n-1 n−1 行,每行包含两个正整数 u u u 和 v v v,表示节点 u u u 和节点 v v v 之间有一条边。
输出格式
输出一个整数,表示满足条件的节点个数。
样例输入
3
BRB
1 2
2 3
样例输出
2
数据范围
1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105
题解
可以使用深度优先搜索(DFS)来解决这个问题。对于每个节点,维护一个长度为 2 2 2 的数组 t t t,其中 t t t 表示以该节点为根的子树中是否包含黑色节点, t t t 表示以该节点为根的子树中是否包含红色节点。
在 DFS 过程中,首先根据当前节点的颜色更新 t t t 数组。然后递归地处理当前节点的每个子节点,并将子节点的 t t t 数组与当前节点的 t t t 数组进行按位或操作,更新当前节点的 t t t 数组。
最后,如果当前节点的 t t t 数组中 t t t 和 t t t 都为 1 1 1,说明以该节点为根的子树中同时包含红色节点和黑色节点,将答案加 1 1 1。
时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。
参考代码
- Cpp
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
string s;
int res = 0;
vector<int> dfs(int u, int pre) {
vector<int> t(2);
if (s[u - 1] == 'B')
t[0] = 1;
else
t[1] = 1;
for (int v : g[u]) {
if (v != pre) {
vector<int> child = dfs(v, u);
t[0] |= child[0];
t[1] |= child[1];
}
}
if (t[0] && t[1])
res++;
return t;
}
int main() {
int n;
cin >> n;
cin >> s;
g.resize(n + 1);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
cout << res << endl;
return 0;
}
- Python
n = int(input())
s = input()
g = [[] for _ in range(n + 1)]
for _ in range(n - 1):
u, v = map(int, input().split())
g[u].append(v)
g[v].append(u)
res = 0
def dfs(u, pre):
global res
t = [0, 0]
if s[u - 1] == 'B':
t[0] = 1
else:
t[1] = 1
for v in g[u]:
if v != pre:
child = dfs(v, u)
t[0] |= child[0]
t[1] |= child[1]
if t[0] and t[1]:
res += 1
return t
dfs(1, 0)
print(res)
- Java
import java.util.*;
public class Main {
static List<List<Integer>> g;
static String s;
static int res = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
s = sc.next();
g = new ArrayList<>();
for (int i = 0; i <= n; i++) {
g.add(new ArrayList<>());
}
for (int i = 0; i < n - 1; i++) {
int u = sc.nextInt();
int v = sc.nextInt();
g.get(u).add(v);
g.get(v).add(u);
}
dfs(1, 0);
System.out.println(res);
}
static int[] dfs(int u, int pre) {
int[] t = new int[2];
if (s.charAt(u - 1) == 'B')
t[0] = 1;
else
t[1] = 1;
for (int v : g.get(u)) {
if (v != pre) {
int[] child = dfs(v, u);
t[0] |= child[0];
t[1] |= child[1];
}
}
if (t[0] == 1 && t[1] == 1)
res++;
return t;
}
}
🧀 04.LYA的因子数量查询
问题描述
LYA有一个非常长的数组,数组中的元素都是正整数,且每个元素的取值范围为 1 1 1 到 10 10 10。为了方便描述,数组以连续段的形式给出,每个连续段用一个二元组 ( u i , v i ) (u_i, v_i) (ui,vi) 表示,其中 u i u_i ui 表示该段中的元素值, v i v_i vi 表示该段的长度。
现在,LYA需要进行 q q q 次查询,每次查询给定一个区间 [ l , r ] [l, r] [l,r],请你帮助她计算出该区间内所有元素的乘积的因子数量。由于答案可能很大,请对 1 0 9 + 7 10^9+7 109+7 取模后输出。
输入格式
第一行包含两个正整数 n n n 和 m m m,分别表示数组的长度和连续段的数量。
接下来 m m m 行,每行包含两个正整数 u i u_i ui 和 v i v_i vi,表示一个连续段。
第 m + 2 m+2 m+2 行包含一个正整数 q q q,表示查询的次数。
接下来 q q q 行,每行包含两个正整数 l l l 和 r r r,表示一次查询的区间。
输出格式
输出共 q q q 行,每行输出一个整数,表示对应查询区间内所有元素的乘积的因子数量对 1 0 9 + 7 10^9+7 109+7 取模后的结果。
样例输入
6 3
1 2
2 1
3 3
2
1 3
2 6
样例输出
2
8
数据范围
1
≤
n
≤
1
0
14
1 \leq n \leq 10^{14}
1≤n≤1014
1
≤
m
,
q
≤
1
0
5
1 \leq m, q \leq 10^5
1≤m,q≤105
1
≤
u
i
≤
10
1 \leq u_i \leq 10
1≤ui≤10
1
≤
v
i
≤
1
0
9
1 \leq v_i \leq 10^9
1≤vi≤109
1
≤
l
≤
r
≤
n
1 \leq l \leq r \leq n
1≤l≤r≤n
保证所有
v
i
v_i
vi 之和等于
n
n
n。
题解
本题可以使用线段树来解决。首先,我们可以预处理出每个数字
1
1
1 到
10
10
10 的质因子分解结果,存储在一个字典 factors_dict
中。然后,我们可以构建一棵线段树,树上的每个节点维护一个字典,表示该节点对应区间内每个质因子的次数。
对于每个连续段 ( u i , v i ) (u_i, v_i) (ui,vi),我们可以将其对应的质因子次数加到线段树的对应区间上。
对于每次查询 [ l , r ] [l, r] [l,r],我们可以通过线段树的区间查询,得到区间 [ l , r ] [l, r] [l,r] 内每个质因子的次数。然后,我们可以根据质因子次数计算出因子数量。设区间 [ l , r ] [l, r] [l,r] 内第 i i i 个质因子的次数为 c i c_i ci,则因子数量为 ∏ i ( c i + 1 ) \prod_{i} (c_i+1) ∏i(ci+1)。
最后,将因子数量对 1 0 9 + 7 10^9+7 109+7 取模后输出即可。
时间复杂度:
O
(
(
n
+
q
)
log
n
)
O((n+q) \log n)
O((n+q)logn),其中
n
n
n 为数组长度,
q
q
q 为查询次数。
空间复杂度:
O
(
n
)
O(n)
O(n)。
参考代码
from collections import defaultdict
MOD = 10 ** 9 + 7
def prime_factors(n):
factors = {}
i = 2
while i * i <= n:
while n % i == 0:
factors[i] = factors.get(i, 0) + 1
n //= i
i += 1
if n > 1:
factors[n] = 1
return factors
factors_dict = {i: prime_factors(i) for i in range(1, 11)}
def build_tree(node, start, end):
if start == end:
tree[node] = defaultdict(int)
return
mid = (start + end) // 2
build_tree(node * 2, start, mid)
build_tree(node * 2 + 1, mid + 1, end)
tree[node] = defaultdict(int)
def update_tree(node, start, end, l, r, val):
if r < start or end < l:
return
if l <= start and end <= r:
for fc, pow in factors_dict[val].items():
tree[node][fc] += pow * (end - start + 1)
return
mid = (start + end) // 2
update_tree(node * 2, start, mid, l, r, val)
update_tree(node * 2 + 1, mid + 1, end, l, r, val)
for fc in set(tree[node * 2]) | set(tree[node * 2 + 1]):
tree[node][fc] = tree[node * 2][fc] + tree[node * 2 + 1][fc]
def query_tree(node, start, end, l, r):
if r < start or end < l:
return defaultdict(int)
if l <= start and end <= r:
return tree[node]
mid = (start + end) // 2
left = query_tree(node * 2, start, mid, l, r)
right = query_tree(node * 2 + 1, mid + 1, end, l, r)
factors = defaultdict(int)
for fc in set(left) | set(right):
factors[fc] = left[fc] + right[fc]
return factors
n, m = map(int, input().split())
tree = [None] * (4 * n)
build_tree(1, 1, n)
index = 1
for _ in range(m):
ui, vi = map(int, input().split())
update_tree(1, 1, n, index, index + vi - 1, ui)
index += vi
q = int(input())
for _ in range(q):
l, r = map(int, input().split())
factors = query_tree(1, 1, n, l, r)
ans = 1
for pow in factors.values():
ans = ans * (pow + 1) % MOD
print(ans)
- Java
import java.io.*;
import java.util.*;
public class Main {
static final int MOD = (int) 1e9 + 7;
static Map<Integer, Map<Integer, Integer>> factorsDict;
static Map<Integer, Integer>[] tree;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] line = br.readLine().split(" ");
int n = Integer.parseInt(line[0]);
int m = Integer.parseInt(line[1]);
factorsDict = new HashMap<>();
for (int i = 1; i <= 10; i++) {
factorsDict.put(i, primeFactors(i));
}
tree = new Map[4 * n];
buildTree(1, 1, n);
int index = 1;
for (int i = 0; i < m; i++) {
line = br.readLine().split(" ");
int ui = Integer.parseInt(line[0]);
int vi = Integer.parseInt(line[1]);
updateTree(1, 1, n, index, index + vi - 1, ui);
index += vi;
}
int q = Integer.parseInt(br.readLine());
for (int i = 0; i < q; i++) {
line = br.readLine().split(" ");
int l = Integer.parseInt(line[0]);
int r = Integer.parseInt(line[1]);
Map<Integer, Integer> factors = queryTree(1, 1, n, l, r);
long ans = 1;
for (int pow : factors.values()) {
ans = ans * (pow + 1) % MOD;
}
System.out.println(ans);
}
}
static Map<Integer, Integer> primeFactors(int n) {
Map<Integer, Integer> factors = new HashMap<>();
int i = 2;
while (i * i <= n) {
while (n % i == 0) {
factors.put(i, factors.getOrDefault(i, 0) + 1);
n /= i;
}
i++;
}
if (n > 1) {
factors.put(n, 1);
}
return factors;
}
static void buildTree(int node, int start, int end) {
if (start == end) {
tree[node] = new HashMap<>();
return;
}
int mid = (start + end) / 2;
buildTree(node * 2, start, mid);
buildTree(node * 2 + 1, mid + 1, end);
tree[node] = new HashMap<>();
}
static void updateTree(int node, int start, int end, int l, int r, int val) {
if (r < start || end < l) {
return;
}
if (l <= start && end <= r) {
for (Map.Entry<Integer, Integer> entry : factorsDict.get(val).entrySet()) {
int fc = entry.getKey();
int pow = entry.getValue();
tree[node].put(fc, tree[node].getOrDefault(fc, 0) + pow * (end - start + 1));
}
return;
}
int mid = (start + end) / 2;
updateTree(node * 2, start, mid, l, r, val);
updateTree(node * 2 + 1, mid + 1, end, l, r, val);
for (int fc : new HashSet<>(tree[node * 2].keySet())) {
tree[node].put(fc, tree[node * 2].getOrDefault(fc, 0) + tree[node * 2 + 1].getOrDefault(fc, 0));
}
for (int fc : new HashSet<>(tree[node * 2 + 1].keySet())) {
tree[node].put(fc, tree[node * 2].getOrDefault(fc, 0) + tree[node * 2 + 1].getOrDefault(fc, 0));
}
}
static Map<Integer, Integer> queryTree(int node, int start, int end, int l, int r) {
if (r < start || end < l) {
return new HashMap<>();
}
if (l <= start && end <= r) {
return tree[node];
}
int mid = (start + end) / 2;
Map<Integer, Integer> left = queryTree(node * 2, start, mid, l, r);
Map<Integer, Integer> right = queryTree(node * 2 + 1, mid + 1, end, l, r);
Map<Integer, Integer> factors = new HashMap<>();
for (int fc : new HashSet<>(left.keySet())) {
factors.put(fc, left.getOrDefault(fc, 0) + right.getOrDefault(fc, 0));
}
for (int fc : new HashSet<>(right.keySet())) {
factors.put(fc, left.getOrDefault(fc, 0) + right.getOrDefault(fc, 0));
}
return factors;
}
}
- Cpp
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
const int MOD = 1e9 + 7;
unordered_map<int, unordered_map<int, int>> factorsDict;
vector<unordered_map<int, int>> tree;
unordered_map<int, int> primeFactors(int n) {
unordered_map<int, int> factors;
int i = 2;
while (i * i <= n) {
while (n % i == 0) {
factors[i]++;
n /= i;
}
i++;
}
if (n > 1) {
factors[n] = 1;
}
return factors;
}
void buildTree(int node, int start, int end) {
if (start == end) {
tree[node].clear();
return;
}
int mid = (start + end) / 2;
buildTree(node * 2, start, mid);
buildTree(node * 2 + 1, mid + 1, end);
tree[node].clear();
}
void updateTree(int node, int start, int end, int l, int r, int val) {
if (r < start || end < l) {
return;
}
if (l <= start && end <= r) {
for (auto entry : factorsDict[val]) {
int fc = entry.first;
int pow = entry.second;
tree[node][fc] += pow * (end - start + 1);
}
return;
}
int mid = (start + end) / 2;
updateTree(node * 2, start, mid, l, r, val);
updateTree(node * 2 + 1, mid + 1, end, l, r, val);
for (auto entry : tree[node * 2]) {
int fc = entry.first;
tree[node][fc] += entry.second;
}
for (auto entry : tree[node * 2 + 1]) {
int fc = entry.first;
tree[node][fc] += entry.second;
}
}
unordered_map<int, int> queryTree(int node, int start, int end, int l, int r) {
if (r < start || end < l) {
return {};
}
if (l <= start && end <= r) {
return tree[node];
}
int mid = (start + end) / 2;
auto left = queryTree(node * 2, start, mid, l, r);
auto right = queryTree(node * 2 + 1, mid + 1, end, l, r);
unordered_map<int, int> factors;
for (auto entry : left) {
int fc = entry.first;
factors[fc] += entry.second;
}
for (auto entry : right) {
int fc = entry.first;
factors[fc] += entry.second;
}
return factors;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
for (int i = 1; i <= 10; i++) {
factorsDict[i] = primeFactors(i);
}
int n, m;
cin >> n >> m;
tree.resize(4 * n);
buildTree(1, 1, n);
int index = 1;
for (int i = 0; i < m; i++) {
int ui, vi;
cin >> ui >> vi;
updateTree(1, 1, n, index, index + vi - 1, ui);
index += vi;
}
int q;
cin >> q;
while (q--) {
int l, r;
cin >> l >> r;
auto factors = queryTree(1, 1, n, l, r);
long long ans = 1;
for (auto entry : factors) {
int pow = entry.second;
ans = ans * (pow + 1) % MOD;
}
cout << ans << '\n';
}
return 0;
}
🍅 05.LYA的字符串子序列集合
问题描述
LYA有一个由数字组成的字符串 s s s。她从 s s s 中选出了所有的非空子序列,并将其中满足相邻字符不相同的子序列都加入到了集合 S S S 中。现在她想知道,集合 S S S 中有多少个不同的子序列。注意,允许子序列以 0 0 0 开头,并且以 0 0 0 开头的子序列和不以 0 0 0 开头的子序列是不同的。
输入格式
输入包含一行,为一个由数字组成的字符串 s s s,表示LYA的字符串。字符串 s s s 的长度不超过 1 0 6 10^6 106,可能包含前导 0 0 0。
输出格式
输出包含一行,为一个整数,表示集合 S S S 中不同子序列的数量。答案可能很大,请对 1 0 9 + 7 10^9+7 109+7 取模后输出。
样例输入
12121
样例输出
9
数据范围
0 ≤ s ≤ 1 0 1000000 0 \leq s \leq 10^{1000000} 0≤s≤101000000
题解
这道题可以使用动态规划来解决。设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示考虑前 i i i 个字符,最后一个字符为 j j j 的满足条件的子序列的数量。初始化 d p [ i n t ( x ) ] = 1 dp[int(x)] = 1 dp[int(x)]=1,表示只考虑第一个字符时,以该字符结尾的子序列数量为 1 1 1。对于第 i i i 个字符 c c c,需要更新 d p [ i ] [ j ] dp[i][j] dp[i][j]:
- 如果 j ≠ c j \neq c j=c,则 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i−1][j],因为以 j j j 结尾的子序列数量不变。
- 如果 j = c j = c j=c,则 d p [ i ] [ c ] = ∑ k = 0 9 d p [ i − 1 ] [ k ] − d p [ i − 1 ] [ c ] + 1 dp[i][c] = \sum_{k=0}^9 dp[i-1][k] - dp[i-1][c] + 1 dp[i][c]=∑k=09dp[i−1][k]−dp[i−1][c]+1,因为以 c c c 结尾的子序列数量等于前 i − 1 i-1 i−1 个字符的所有子序列数量,再减去前 i − 1 i-1 i−1 个字符中以 c c c 结尾的子序列数量,最后加上空序列。
最终答案为 ∑ j = 0 9 d p [ n ] [ j ] \sum_{j=0}^9 dp[n][j] ∑j=09dp[n][j],即考虑所有字符后,以任意字符结尾的满足条件的子序列数量之和。时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)。
参考代码
- Python
#include <iostream>
using namespace std;
const int MOD = 1e9 + 7;
int main() {
string s;
cin >> s;
int n = s.length();
long long dp[n + 1][10];
for (int i = 0; i <= n; i++) {
for (int j = 0; j < 10; j++) {
dp[i][j] = 0;
}
}
dp[1][s[0] - '0'] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 0; j < 10; j++) {
if (j != s[i - 1] - '0') {
dp[i][j] = dp[i - 1][j];
}
}
long long sum = 0;
for (int j = 0; j < 10; j++) {
sum = (sum + dp[i - 1][j]) % MOD;
}
dp[i][s[i - 1] - '0'] = (sum - dp[i - 1][s[i - 1] - '0'] + 1 + MOD) % MOD;
}
long long ans = 0;
for (int j = 0; j < 10; j++) {
ans = (ans + dp[n][j]) % MOD;
}
cout << ans << endl;
return 0;
}
- Java
import java.util.Scanner;
public class Main {
static final int MOD = (int) 1e9 + 7;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
int n = s.length();
long[][] dp = new long[n + 1][10];
dp[1][s.charAt(0) - '0'] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 0; j < 10; j++) {
if (j != s.charAt(i - 1) - '0') {
dp[i][j] = dp[i - 1][j];
}
}
long sum = 0;
for (int j = 0; j < 10; j++) {
sum = (sum + dp[i - 1][j]) % MOD;
}
dp[i][s.charAt(i - 1) - '0'] = (sum - dp[i - 1][s.charAt(i - 1) - '0'] + 1 + MOD) % MOD;
}
long ans = 0;
for (int j = 0; j < 10; j++) {
ans = (ans + dp[n][j]) % MOD;
}
System.out.println(ans);
}
}
- Cpp
#include <iostream>
using namespace std;
const int MOD = 1e9 + 7;
int main() {
string s;
cin >> s;
int n = s.length();
long long dp[n + 1];
dp[0] = 1;
int last[10];
for (int i = 0; i < 10; i++) {
last[i] = -1;
}
for (int i = 1; i <= n; i++) {
int digit = s[i - 1] - '0';
if (i == 1 || s[i - 1] != s[i - 2]) {
dp[i] = dp[i - 1] * 2 % MOD;
} else {
dp[i] = dp[last[digit] + 1] * 2 % MOD;
}
if (last[digit] != -1) {
dp[i] -= dp[last[digit]];
}
dp[i] = (dp[i] + MOD) % MOD;
last[digit] = i;
}
cout << (dp[n] - 1 + MOD) % MOD << endl;
return 0;
}
写在最后
📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取,会在飞书进行同步的跟新。