复盘一下真题,用java解答,所有程序仅通过样例,可能存在错误,最后一题暂时没用java重写。
思路借鉴于公众号“万诺coding”
难度:第一、二道是简单题,第三道是略难的简单题,第四道和第五道是中等偏上题。
1.最少修改次数
小美拿到了一个长度为7的字符串。她想知道将该字符串修改为"meituan"至少需要修改多少次?
每次修改,小美可以修改任意一个字符。
输入描述:
输入一个长度为7的字符串,字符串中只包含小写字母。
输出描述:
小美需要修改的次数。
样例输入
meituan
样例输出
0
思路与代码
模拟计数即可。
import java.util.Scanner;
public class N1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 输入目标字符串
String target = "meituan";
// 输入待修改的字符串
String string = scanner.nextLine();
int count = 0;
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) != target.charAt(i)) {
count++;
}
}
System.out.println(count);
scanner.close();
}
}
2.计算实数
小美拿到了一个由复数组成的数组,她想知道其中有多少个实数?
实数:有理数和无理数的总称。其中无理数是无限不循环小数,有理数包括整数和分数。
输入描述:
第一行输入一个正整数,代表数组的大小。
第二行输入n个复数,代表小美拿到的数组。
后台数据保证复数为a或者a+bi的形式,其中a和b为绝对值不超过10^9的整数。
1<= n <=10^5
输出描述:
一个整数,代表实数的数量。
样例输入
4
-5 5-i 6+3i -4+0i
样例输出
2
思路与代码
模拟题。
将所有的"+0i"和"-0i"替换成空字符串,然后判断每个子串是否包含"i"即可。
import java.util.Scanner;
public class N2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取数组大小
int n = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
// 读取复数数组
String[] complexNumbers = scanner.nextLine().split(" ");
int realCount = 0;
// 将所有的"+0i"和"-0i"替换成空字符串,然后判断每个子串是否包含"i"即可。
for (String complexNumber : complexNumbers) {
complexNumber = complexNumber.replace("+0i", "").replace("-0i", "");
if (!complexNumber.contains("i")) {
realCount++;
}
}
System.out.println(realCount);
scanner.close();
}
}
3.还原数组
小美有一个由 n 个互不相等的正整数构成的数组 a,但她一不小心把 a 弄丢了,他想要重新找到 a。
好在她并不是一无所有,她还记得以下有关 a 的信息:
\1. 他完全记得数组 b 的样子,并且 b 是数组 a 删除了某个 a_i 后,剩余的部分做前缀和并打乱的结果。
\2. 他完全记得数组 c 的样子,并且 c 是数组 a 删除了某个 a_j 后,剩余的部分做前缀和并打乱的结果。
(保证两次删除的 a_i 和 a_j 不是同一个 a 中的元素)。
请你帮她还原出 a 数组吧。
补充:前缀和指一个数组的某下标之前的所有数组元素的和(包含其自身)。
输入描述
输入包含三行。
第一行一个正整数 n\ (3 <=n <=10^5),表示数组 a 的长度。
第二行 n-1 个正整数 b_i\ (1 <=b_i <=10^{14}),表示题中所述数组 b。
第二行 n-1 个正整数 c_i\ (1 <=c_i <=10^{14}),表示题中所述数组 c。
(输入保证有唯一解)
输出描述
输出一行 n 个整数,表示你还原出的 a 数组。
样例输入
4
8 18 14
15 9 1
样例输出
1 8 6 4
思路与代码
思维题。
前缀和是有序的,因此可以直接排序。如此一来可以利用前缀和还原数组,我们以第一个数组nums1为基准,遍历第二个数组nums2,如果找到一个数字x是不存在于nums1的,假设我们定义x的下标是i,那么我们可以确定,x插入到nums1中,nums2[i+1]对应位置的前面。
import java.util.*;
public class N3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] b = new int[n-1];
int[] c = new int[n-1];
for (int i = 0; i < n-1; i++) {
b[i] = scanner.nextInt();
}
for (int i = 0; i < n-1; i++) {
c[i] = scanner.nextInt();
}
// Sort the arrays
Arrays.sort(b);
Arrays.sort(c);
int[] nums1 = new int[n - 1];
int[] nums2 = new int[n - 1];
nums1[0] = b[0];
nums2[0] = c[0];
for (int i = 1; i < n - 1; i++) {
nums1[i] = b[i] - b[i - 1];
nums2[i] = c[i] - c[i - 1];
}
Set<Integer> set = new HashSet<>();
for (int num : nums1) {
set.add(num);
}
for(int i = 0; i < n-1; i++){
// 如果nums1 不包含 nums2[i],则说明这个值是缺失的那个并且唯一
if(!set.contains(nums2[i])){ // 8 18 14(6,4) 15 9 1(8,6)
if(i == n-1){
// 先打印完nums1,再打印nums2[i],例如1 8 6 4
for (int num : nums1) {
System.out.print(num + " ");
}
// 再打印nums2[i]
System.out.print(nums2[i]);
break;
}else{ // 没到末尾就出现了唯一的缺少值
int index = Arrays.asList(nums1).indexOf(nums2[i + 1]);
// 先打印nums1[0] 到[index]的结果,再打印nums2[i],再打印nums1[index]到nums1末尾
for (int j = 0; j <= index; j++) {
System.out.print(nums1[j] + " ");
}
// 再打印nums2[i]
System.out.print(nums2[i] + " ");
// 最后打印nums1[index]到nums1末尾
for (int j = index + 1; j < nums1.length; j++) {
System.out.print(nums1[j] + " ");
}
break;
}
}
}
}
}
4.小美点外卖
小美经常去美团上点外卖,她给自己规划了每天会在哪些商家中选择一家来点。
由于怕吃腻,小美决定不会连续两天点同一个商家。现在请你判断小美有多少种不同的选择方案?
输入格式
第一行输入一个正整数n,代表小美点外卖的天数。
接下来的n行,每行输入一个长度不超过 20 的字符串,代表小美每天的选项。相同的字符代表同一个商家。
1<=n <=10^5
保证每个字符串内的字符都不同。
输出格式
输出一个整数,代表小美点外卖的方案数。由于答案过大,请对10^9+7取模。
示例1
输入
2
ab
bcd
输出
5
说明
方案 1:第一天点商家 a,第二天点商家 b。
方案 2:第一天点商家 a,第二天点商家 c。
方案 3:第一天点商家 a,第二天点商家 d。
方案 4:第一天点商家 b,第二天点商家 c。
方案 5:第一天点商家 b,第二天点商家 d。
思路与代码
动态规划。
每一天的选择是:选择当天的任意一个商家,只要满足与上一天的商家不一样即可。
f[i,j] 含义为,上一天选择的商家是j,第i天往后考虑的方案数。
f[i,j] = Σf[i+1,k],for k in stores[i] && k!=j
import java.util.HashMap;
import java.util.Scanner;
public class N4 {
static int mod = 1000000007; // 定义模数
static HashMap<String, Integer> dp = new HashMap<>(); // 用于记忆化搜索的HashMap,避免重复计算
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 读取n的值
scanner.nextLine(); // 读取换行符
String[] stores = new String[n]; // 创建存储每天的商店名称的数组
for (int i = 0; i < n; i++) {
stores[i] = scanner.nextLine(); // 读取每天的每个商店的名称
}
scanner.close(); // 关闭Scanner
// (当前商店的下标,前一个选择的字符,所有商店可选择的值)
int result = dfs(0, "", stores); // 计算结果
System.out.println(result); // 输出结果
}
// 深度优先搜索函数(当前访问的天数i,前一天选择的商店c,可选择的商店数组)
static int dfs(int i, String c, String[] stores) {
// 如果已经访问完所有商店,则返回1表示这是一种有效的访问方式
if (i == stores.length) return 1;
// 如果已经计算过这个状态(当前商店索引i和当前字符c),则返回已经计算过的结果,避免重复计算
if (dp.containsKey(i + "," + c)) return dp.get(i + "," + c);
int res = 0; // 用于存储当前状态的结果
// 如果当前字符不为空(表示上一个商店已经选择了一个字符),进入以下循环
if (!c.isEmpty()) {
// 遍历当前商店的字符
for (int j = 0; j < stores[i].length(); j++) {
// 如果当前商店字符与前一个字符不同,可以选择这个字符继续访问下一个商店
if (stores[i].charAt(j) != c.charAt(0)) {
// 递归调用dfs函数,更新结果并取模运算(下一个商店,当前商店选择的值,商店的可选择数组)
res += dfs(i + 1, Character.toString(stores[i].charAt(j)), stores);
res %= mod;
}
}
} else { // 如果当前字符为空(表示还未选择字符),进入以下循环
// 遍历当前商店的字符
for (int j = 0; j < stores[i].length(); j++) {
// 递归调用dfs函数,更新结果并取模运算(下一个商店,当前商店选择的值,商店的可选择数组)
res += dfs(i + 1, Character.toString(stores[i].charAt(j)), stores);
res %= mod;
}
}
// 将当前状态的结果存入记忆化搜索HashMap中,以便后续查询
dp.put(i + "," + c, res);
// 返回当前状态的结果
return res;
}
}
5.获胜的概率
在一个n行m列的矩阵中,小美初始在第x_1行第y_1列的位置,小团初始在x_2行第y_2列的位置。
小美每一步可以向左下、下、右下三个方向走一个格子,小团可以向左上、上、右上三个方向走一个格子。
小美先行动,小团后行动。如果两人在同一个格子相遇了,那么她们将结束游戏并获胜。如果最终都没在同一个格子,则她们失败。
但是两人都不知道对方的位置,因此她们会随机选择一个方向前进(如果在最左那一列,则不能往左上/左下。如果在最右那一列,则不能往右上/右下)。请你计算小美和小团最终获胜的概率。
输入描述
输入六个正整数n,m,x_1,y_1,x_2,y_2,用空格隔开。分别代表矩阵的行数、列数,以及小美和小团的初始位置。
1<=n,m <=3000
1<=x_1,x_2 <=n
1<=y_1,y_2 <=m
输出描述
小美和小团最终获胜的概率。可以证明,这个答案一定是一个有理数,你需要输出其对10^9+7取模的结果。
分数取模的定义:假设答案是x/y,那么其对p取模的答案是找到一个整数a满足a∈[0,p-1]且a*y对p取模等于x。
示例1
输入
3 3 1 2 2 2
输出
333333336
思路与代码
动态规划。
dp[i,j]的含义是:走到(i,j)的概率。需要注意的是,对于非边缘列(1 < j < m),小美向左下、下、右下移动的概率都是相等的,即每个方向的概率为当前位置概率除以3。因为非边缘列的位置有三个可移动的方向。对于边缘列(j == 1 或 j == m),只有两个方向可以选择,每个方向的概率为当前位置概率除以2。
对于小美和小团分别跑一次dp。
由于最终相遇必然是在中点,我们枚举累乘dp1i和dp2i,并且累加这个答案即可。
【逆元大家需要把模板背下来】
// 请到公众号去查看