腾讯
腾讯2020校园招聘-后台
解压字符串
小Q想要给他的朋友发送一个神秘字符串,但是他发现字符串的过于长了,于是小Q发明了一种压缩算法对字符串中重复的部分进行了压缩,对于字符串中连续的m个相同字符串S将会压缩为m|S,例如字符串ABCABCABC将会被压缩为[3|ABC],现在小Q的同学收到了小Q发送过来的字符串,你能帮助他进行解压缩么?
输入描述:
输入第一行包含一个字符串s,代表压缩后的字符串。S的长度<=1000;S仅包含大写字母、[、]、|;解压后的字符串长度不超过100000;压缩递归层数不超过10层;
输出描述:
输出一个字符串,代表解压后的字符串。
输入例子1:
HG[3|B[2|CA]]F
输出例子1:
HGBCACABCACABCACAF
例子说明1
HG[3|B[2|CA]]F−>HG[3|BCACA]F−>HGBCACABCACABCACAF
import java.util.Collections;
import java.util.Scanner;
public class Main {
private static String getstr(String words){
while (words.contains("]")){
int right = words.indexOf("]");
int left = words.lastIndexOf("[",right);
String repeatStr = words.substring(left + 1, right);
String[] split = repeatStr.split("\\|"); //字符串拆分成数组
words = words.replace("[" + repeatStr+"]",
String.join("", Collections.nCopies(Integer.parseInt(split[0]), split[1])));//把[***]替换成split[0]次重复的split[1]
}
return words;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str = in.nextLine();
System.out.println(getstr(str));
}
}
找能看到几栋高楼
小Q在周末的时候和他的小伙伴来到大城市逛街,一条步行街上有很多高楼,共有n座高楼排成一行。
小Q从第一栋一直走到了最后一栋,小Q从来都没有见到这么多的楼,所以他想知道他在每栋楼的位置处能看到多少栋楼呢?(当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住)
输入描述:
输入第一行将包含一个数字n,代表楼的栋数,接下来的一行将包含n个数字wi(1<=i<=n),代表每一栋楼的高度。1<=n<=100000;1<=wi<=100000;
输出描述:
输出一行,包含空格分割的n个数字vi,分别代表小Q在第i栋楼时能看到的楼的数量。
输入例子1:
6
5 3 8 3 2 5
输出例子1:
向右看:
stack中插入可以看到的楼的index,如果原始数组中前面的数大于后面的数 则表示挡住了后面的数,此时把stack中后面的数的index弹出,要循环遍历每一个后面的数(while),只要有的把遮挡住的index弹出,循环结束后压入新的index,如果不大于后面的数则表示没有遮挡住,直接把index压入stack即可,要循环遍历每一个后面的数(while),rightLook中从后往前记录stack的长度表示可以看到的楼的栋数。
向左看:
stack中插入可以看到的楼的index,如果原始数组中前面的数大于后面的数 则表示挡住了后面的数,此时把stack中后面的数的index弹出,要循环遍历每一个后面的数(while),只要有的把遮挡住的index弹出,循环结束后压入新的index,如果不大于后面的数则表示没有遮挡住,直接把index压入stack即可,要循环遍历每一个后面的数(while),rightLook中从前往后记录stack的长度表示可以看到的楼的栋数
3 3 5 4 4 4
例子说明1:
当小Q处于位置3时,他可以向前看到位置2,1处的楼,向后看到位置4,6处的楼,加上第3栋楼,共可看到5栋楼。当小Q处于位置4时,他可以向前看到位置3处的楼,向后看到位置5,6处的楼,加上第4栋楼,共可看到4栋楼
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int len = sc.nextInt();
int[] arr = new int[len];
for(int i = 0 ; i < len ; i++){
arr[i] = sc.nextInt();
}
// stack中要保存的是 能看见的楼的 index
int[] rightLook = new int[len]; //保存的是第i个位置的右边能看到几栋楼
//首先让rightLook的最后一个位置等于stack.size=0 ,因为最后一个的右边没有了
//让stack保存最后一个位置,判断末二个位置上的是否大于它,大于则去掉最后一个位置用末二个位置代替之存在stack中,表示末二个位置上的数遮住了最后一个数,就看不见了。如果不大于它,则直接存入stack表示两个位置上的数可以同时被看到。每次循环要判断右侧已存在与stack中的所有元素,保证大于已存在的元素则替换之,不大于已存在的元素则直接添加即可 。
Stack<Integer> stack = new Stack<Integer>();
for(int i = len - 1 ; i >= 0 ; i--){
rightLook[i] = stack.size();
while((!stack.isEmpty()) && (arr[i] >= arr[stack.peek()])){
stack.pop();
}
stack.push(i);
}
stack.clear();
for(int i = 0 ; i < len ; i++){
int total = rightLook[i] + 1 + stack.size();
while((!stack.isEmpty()) && (arr[i] >= arr[stack.peek()])){
stack.pop();
}
System.out.print(total + " ");
stack.push(i);
}
}
}
`
最少休息天数
由于业绩优秀,公司给小Q放了 n 天的假,身为工作狂的小Q打算在在假期中工作、锻炼或者休息。他有个奇怪的习惯:不会连续两天工作或锻炼。只有当公司营业时,小Q才能去工作,只有当健身房营业时,小Q才能去健身,小Q一天只能干一件事。给出假期中公司,健身房的营业情况,求小Q最少需要休息几天。
输入描述:
第一行一个整数 表示放假天数第二行 n 个数 每个数为0或1,第 i 个数表示公司在第 i 天是否营业第三行 n 个数 每个数为0或1,第 i 个数表示健身房在第 i 天是否营业(1为营业 0为不营业)
输出描述:
一个整数,表示小Q休息的最少天数
输入例子1:
4
1 1 0 0
0 1 1 0
输出例子1:
2
例子说明1:
休息 锻炼 工作
0 0 0
0 0 1(因为可以工作,所以在前一天的休息和锻炼中取最大值+1)
1 2 1(因为既可以工作也可以锻炼,则工作同上,锻炼则在前一天的休息和工作中取最大值+1)
2 2 0(因为只能锻炼,则在前一天的工作和休息中取最大值+1)
2 0 0(既不能工作也不能锻炼,则休息取最大值表示前面工作的最大天数即可)
小Q可以在第一天工作,第二天或第三天健身,小Q最少休息2天
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int [] keeparr = new int[n];
int [] workarr = new int[n];
for(int i = 0 ; i <n ; i++){
workarr[i] = sc.nextInt();
}
for(int i = 0 ; i <n ; i++){
keeparr[i] = sc.nextInt();
}
/*重点 :记录子问题的解 */
int [][] days = new int [n+1][3];
for(int i = 1 ; i<= n ;i++){
// 可以选择去健身
if(keeparr[i-1] == 1){
//第i天去健身 那么i-1必定是休息或工作,取最大值+1
days[i][1] = Math.max(days[i-1][0],days[i-1][2])+1;
}
if(workarr[i-1] == 1){
//第i天去工作 那么i-1天必定是休息或健身,取最大值+1
days[i][2] = Math.max(days[i-1][0],days[i-1][1])+1;
}
//休息之前做什么事都可以,取前面工作天数的最大值即可,因为求的是最少休息天数,即最大做事情的天数
days[i][0] =Math.max(Math.max(days[i-1][0], days[i-1][1]), days[i-1][2]);
}
//result 就是所有情况分析后 能有事做的最大天数
int result = Math.max(Math.max(days[n][2], days[n][1]), days[n][0]);
//n-result 就是最小的休息数
System.out.println(n-result);
}
}
最少需要几个视野范围
小Q在进行一场竞技游戏,这场游戏的胜负关键就在于能否能争夺一条长度为L的河道,即可以看作是[0,L]的一条数轴。这款竞技游戏当中有n个可以提供视野的道具−真视守卫,第i个真视守卫能够覆盖区间[xi,yi]。现在小Q想知道至少用几个真视守卫就可以覆盖整段河道。
输入描述:
输入包括n+1行。第一行包括两个正整数n和L(1<=n<=105,1<=L<=109)接下来的n行,每行两个正整数xi,yi(0<=xi<=yi<=109),表示第i个真视守卫覆盖的区间。
输出描述:
一个整数,表示最少需要的真视守卫数量, 如果无解, 输出-1。
输入例子1:
4 6
3 6
2 4
0 2
4 7
输出例子1:
3
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int L=in.nextInt();
int[][] temp=new int[n][2];
for(int i=0;i<n;i++) {
for(int j=0;j<2;j++) {
temp[i][j]=in.nextInt();
}
}
//。获得了数组,进行排序
Arrays.sort(temp,new Comparator<int[]>() {
public int compare(int[] o1, int[] o2) {
return o1[0]==o2[0]?o1[1]-o2[1]:o1[0]-o2[0];
}
});
int index=0;
int count=0;
int pre=0; //右边界
while(pre<L) {
if(temp[index][0]>pre) {
System.out.println(-1);
}
int max=0;
while(index<n&&temp[index][0]<=pre) {
max=Math.max(max, temp[index][1]);
index++;
}
count++;
pre=max;
if(pre>=L) {
System.out.println(count);
return;
}
if(index>=n) {
System.out.println(-1);
return;
}
}
}
}
腾讯2018春招技术类编程题汇总
-1 -2 3 4或 -1 2 -3 4 -5 6的和
小Q定义了一种数列称为翻转数列:给定整数n和m, 满足n能被2m整除。对于一串连续递增整数数列1, 2, 3, 4…, 每隔m个符号翻转一次, 最初符号为’-’;。
例如n = 8, m = 2, 数列就是: -1, -2, +3, +4, -5, -6, +7, +8.
而n = 4, m = 1, 数列就是: -1, +2, -3, + 4.
小Q现在希望你能帮他算算前n项和为多少。
输入描述:
输入包括两个整数n和m(2 <= n <= 109, 1 <= m), 并且满足n能被2m整除。
输出描述:
输出一个整数, 表示前n项和。
输入例子1:
8 2
输出例子1:
8
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
long n=sc.nextInt();
long m=sc.nextInt();
System.out.println(m*(n/2)); //公式求-1到-n,每隔m个翻转符号求和
}
}
从大到小间隔取数并分别求和
牛牛和羊羊正在玩一个纸牌游戏。这个游戏一共有n张纸牌, 第i张纸牌上写着数字ai。
牛牛和羊羊轮流抽牌, 牛牛先抽, 每次抽牌他们可以从纸牌堆中任意选择一张抽出, 直到纸牌被抽完。
他们的得分等于他们抽到的纸牌数字总和。
现在假设牛牛和羊羊都采用最优策略(拿最大的), 请你计算出游戏结束后牛牛得分减去羊羊得分等于多少。
输入描述:
输入包括两行。第一行包括一个正整数n(1 <= n <= 105),表示纸牌的数量。第二行包括n个正整数ai(1 <= ai <= 109),表示每张纸牌上的数字。
输出描述:
输出一个整数, 表示游戏结束后牛牛得分减去羊羊得分等于多少。
输入例子1:
3
2 7 4
输出例子1:
5
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n= sc.nextInt();
int[] nums = new int[n];
for(int i=0;i<n;i++){
nums[i] = sc.nextInt();
}
Arrays.sort(nums);
long ox=0;
long sheep=0;
for(int i=n-1;i>=0;i=i-2){
ox = ox+(long)nums[i];
if(i-1>=0){ //i-1是sheep要取的位置 要保证它大于等于0
sheep = sheep+(long)nums[i-1];
}
}
long diff = ox-sheep;
System.out.println(diff);
}
}
小Q的父母要出差N天,走之前给小Q留下了M块巧克力。小Q决定每天吃的巧克力数量不少于前一天吃的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,请问他第一天最多能吃多少块巧克力
输入描述:
每个输入包含一个测试用例。每个测试用例的第一行包含两个正整数,表示父母出差的天数N(N<=50000)和巧克力的数量M(N<=M<=100000)。
输出描述:
输出一个数表示小Q第一天最多能吃多少块巧克力。
输入例子1:
3 7
输出例子1:
4
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
//二分法,定位第一天可以吃的巧克力的个数,第一天最少吃1块,最多吃m块
int left = 1, right = m;
while(left < right) {
int mid = left + (right - left + 1) / 2;
//题意:找到最后一个小于等于sum(mid)的mid,就是第一天能够吃的最多的巧克力数
if(sum(mid, n) > m) {
right = mid - 1;
} else {
left = mid;
}
}
System.out.println(left);
}
//第一天吃s块,n天总共最少吃几块
private static int sum(int s, int n) {
int sum = 0;
for(int i = 0; i < n; i++) {
sum += s;
s = (s + 1) >> 1; //右移相当于/2但可以保证为整数,不少于s的一半
}
return sum;
}
}
xx
小Q有X首长度为A的不同的歌和Y首长度为B的不同的歌,现在小Q想用这些歌组成一个总长度正好为K的歌单,每首歌最多只能在歌单中出现一次,在不考虑歌单内歌曲的先后顺序的情况下,请问有多少种组成歌单的方法。
每个输入包含一个测试用例。每个测试用例的第一行包含一个整数,表示歌单的总长度K(1<=K<=1000)。接下来的一行包含四个正整数,分别表示歌的第一种长度A(A<=10)和数量X(X<=100)以及歌的第二种长度B(B<=10)和数量Y(Y<=100)。保证A不等于B。
输出描述:
输出一个整数,表示组成歌单的方法取模。因为答案可能会很大,输出对1000000007取模的结果。
输入例子1:
5
2 3 3 3
输出例子1:
9
import java.util.*;
public class Main{
public static int mod=1000000007;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int k=sc.nextInt();
int a=sc.nextInt();
int x=sc.nextInt();
int b=sc.nextInt();
int y=sc.nextInt();
int[] dp = new int[k+1];
dp[0] = 1;
for(int i=0;i<x;i++){
for(int j=k;j>=a;j--){
dp[j]=(dp[j]+dp[j-a]) % mod;
}
}
for(int i=0;i<y;i++){
for(int j=k;j>=b;j--){
dp[j]=(dp[j]+dp[j-b]) % mod;
}
}
System.out.println(dp[k]);
}
}
限制条件下 求最优解
小Q的公司最近接到m个任务, 第i个任务需要xi的时间去完成, 难度等级为yi。
小Q拥有n台机器, 每台机器最长工作时间zi, 机器等级wi。
对于一个任务,它只能交由一台机器来完成, 如果安排给它的机器的最长工作时间小于任务需要的时间, 则不能完成,如果完成这个任务将获得200 * xi + 3 * yi收益。
对于一台机器,它一天只能完成一个任务, 如果它的机器等级小于安排给它的任务难度等级, 则不能完成。
小Q想在今天尽可能的去完成任务, 即完成的任务数量最大。如果有多种安排方案,小Q还想找到收益最大的那个方案。小Q需要你来帮助他计算一下。
输入描述:
输入包括N + M + 1行,输入的第一行为两个正整数n和m(1 <= n, m <= 100000), 表示机器的数量和任务的数量。接下来n行,每行两个整数zi和wi(0 < zi < 1000, 0 <= wi <= 100), 表示每台机器的最大工作时间和机器等级。接下来的m行,每行两个整数xi和yi(0 < xi < 1000, 0 <= yi<= 100), 表示每个任务需要的完成时间和任务的难度等级。
输出描述:
输出两个整数, 分别表示最大能完成的任务数量和获取的收益。
输入例子1:
1 2
100 3
100 2
100 1
输出例子1:
1 20006
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[][] machine = new int[n][2];
int[][] task = new int[m][2];
for (int i = 0; i < n; i++) {
for (int j = 0; j < 2; j++) {
machine[i][j] = scanner.nextInt();
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < 2; j++) {
task[i][j] = scanner.nextInt();
}
}
Comparator<int[]> compa = new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o2[1] - o1[1] : o2[0] - o1[0];
}
};
Arrays.sort(machine, compa);
Arrays.sort(task, compa);
int[] cnt = new int[101];
int count = 0;
long ans = 0;
for (int i = 0, j = 0; i < m; i++) {
//遍历每一个机器,找出时长上能完成task[i]的机器,并记录当前机器的难度在cnt数组中
while (j < n && machine[j][0] >= task[i][0]) {
cnt[machine[j][1]]++;
j++;
}
//遍历cnt数组,找出难度上能完成task[i]的机器
for (int k = task[i][1]; k <= 100; k++) {
if (cnt[k] != 0){
count++;
cnt[k]--;
ans = ans + 200 * task[i][0] + 3 * task[i][1];
break;
}
}
}
System.out.println(count + " " + ans);
}
}
画家小Q又开始他的艺术创作。小Q拿出了一块有NxM像素格的画板, 画板初始状态是空白的,用’X’表示。
小Q有他独特的绘画技巧,每次小Q会选择一条斜线, 如果斜线的方向形如’/’,即斜率为1,小Q会选择这条斜线中的一段格子,都涂画为蓝色,用’B’表示;如果对角线的方向形如’’,即斜率为-1,小Q会选择这条斜线中的一段格子,都涂画为黄色,用’Y’表示。
如果一个格子既被蓝色涂画过又被黄色涂画过,那么这个格子就会变成绿色,用’G’表示。
小Q已经有想画出的作品的样子, 请你帮他计算一下他最少需要多少次操作完成这幅画。
输入描述:
每个输入包含一个测试用例。每个测试用例的第一行包含两个正整数N和M(1 <= N, M <= 50), 表示画板的长宽。接下来的N行包含N个长度为M的字符串, 其中包含字符'B','Y','G','X',分别表示蓝色,黄色,绿色,空白。整个表示小Q要完成的作品。
输出描述:
输出一个正整数, 表示小Q最少需要多少次操作完成绘画。
输入例子1:
4 4
YXXB
XYGX
XBYY
BXXY
输出例子1:
3
例子说明1:
XXXXXXXXXXXXXXXX->YXXXXYXXXXYXXXXY->YXXBXYBXXBYXBXXY->YXXBXYGXXBYYBXXY
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int m = input.nextInt();
char[][] p = new char[n][m];
for(int i=0;i<n;i++){
p[i] = input.next().toCharArray();
}
int res= 0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++) {
int t = 0; //处理对角线问题
if (p[i][j] == 'Y' || p[i][j] == 'G') {
res++;
while (i + t < n && j + t < m && (p[i+t][j+t] == 'Y' || p[i+t][j+t] == 'G')) {
if (p[i+t][j+t] == 'G') p[i+t][j+t] = 'B';
else p[i+t][j+t] = 'X'; //循环消除对角线上的操作,逆推操作次数
t++;
}
}
t=0;
if (p[i][j] == 'B' || p[i][j] == 'G') {
res++;
while (i + t < n && j - t >= 0 && (p[i+t][j-t] == 'B' || p[i+t][j-t] == 'G')) {
if (p[i+t][j-t] == 'G') p[i+t][j-t] = 'Y';
else p[i+t][j-t] = 'X';
t++;
}
}
}
}
System.out.println(res);
}
}
腾讯2017校招开发工程师笔试试卷(二)
有规律的数组
假定一种编码的编码范围是a-y的25个字母,从1位到4位的编码,如果我们把该编码按字典序排序,形成一个数组如下:
a,aa,aaa,aaaa,aaab,aaac,…,…,b,ba,baa,baaa,baab,baac,… …,yyyw,yyyx,yyyy
其中a的Index为0,aa的Index为1,aaa的Index为2,以此类推。
编写一个函数,输入是任意一个编码,输出这个编码对应的index,如:
输入:baca
输出:16328
链接:https://www.nowcoder.com/questionTerminal/9193ebe20b0f47bc89469fb5674cc0d8
来源:牛客网
public class LexicographicalCode {
/**
* 假定一种编码的编码范围是a ~ y的25个字母,从1位到4位的编码,
* 如果我们把该编码按字典序排序,形成一个数组如下:
* a, aa, aaa, aaaa, aaab, aaac, … …, b, ba, baa, baaa, baab, baac … …, yyyw, yyyx, yyyy
* 其中a的Index为0,aa的Index为1,aaa的Index为2,
* 以此类推。 编写一个函数,输入是任意一个编码,
* 输出这个编码对应的Index.
*
* @param args
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String i = scanner.next();
System.out.println(codeIndex(i));
}
/**
* 思路:排列组合的思想<br>
* a和b之间 有25+25*25+25*25*25个数 (25:aa,ab……ay),<br>
* (25*25: aaa,aab……aba……ayy);(25*25*25: aaaa,……ayyy)<br>
* 所以b的位置是 a+ 25+25*25+25*25*25+1<br>
* 以此类推:ab = aa +25+25*25 +1 <br>
* aab = aaa + 25 + 1<br>
* aaab + aaaa + 1;<br>
* 然后索引从a, aa,aaa开始 0,1,2,可以认为是长度-1<br>
* @param code
* <a href="/profile/547241" data-card-uid="547241" class="" target="_blank" data-card-index="16">@return
*/
public static int codeIndex(String code) {
int factor[] = {1 + 25 + 25 * 25 + 25 * 25 * 25, 1 + 25 + 25 * 25, 1 + 25, 1};
char[] codeArray = code.toCharArray();
int index = 0;
int len = 0;
for (int i = 0; i < codeArray.length; i++) {
index += factor[len++] * (codeArray[i] - 'a');
}
return index + (len - 1);
}
public static String deCode(int index) {
int factor[] = {1 + 25 + 25 * 25 + 25 * 25 * 25, 1 + 25 + 25 * 25, 1 + 25, 1};
StringBuilder stringBuilder = new StringBuilder();
int i = 0;
while (index > 0) {
stringBuilder.append((char) ('a' + index / factor[i]));
index %= factor[i++];
index--;
}
return stringBuilder.toString();
}
}
做任务并设置状态
游戏里面有很多各种各样的任务,其中有一种任务玩家只能做一次,这类任务一共有1024个,任务ID范围[1,1024].请用32个unsigned int类型来记录着1024个任务是否已经完成。初始状态为未完成。
输入两个参数,都是任务ID,需要设置第一个ID的任务为已经完成;并检查第二个ID的任务是否已经完成。
输出一个参数,如果第二个ID的任务已经完成输出1,如果未完成输出0,。如果第一或第二个ID不在[1,1024]范围,则输出1.
如:
输入:1024 1024
输出:1
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
String id1 = scanner.next();
String id2 = scanner.next();
getAnswer(id1, id2);
}
public static void getAnswer(String s1, String s2){
// HashMap<String, String> index = new HashMap<>();
//int id = 1;
//for(int i=1; i<33; i++){
// for(int j=1; j<33; j++){
// index.put(String.valueOf(id),String.valueOf(i)+String.valueOf(j));
// id++;
// }
// }
if(!index.containsKey(s1)||!index.containsKey(s2)){
System.out.println(-1);
} else if(index.get(s1).equals(index.get(s2))){
System.out.println(1);
} else {
System.out.println(0);
}
}
}
字节跳动
2019春招研发部分编程题汇总
检查字符串拼写错误
\1. 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello
\2. 两对一样的字母(AABB型)连在一起,一定是拼写错误,去掉第二对的一个字母就好啦:比如 helloo -> hello
\3. 上面的规则优先“从左到右”匹配,即如果是AABBCC,虽然AABB和BBCC都是错误拼写,应该优先考虑修复AABB,结果为AABCC
输入描述:
第一行包括一个数字N,表示本次用例包括多少个待校验的字符串。后面跟随N行,每行为一个待校验的字符串。
输出描述:
N行,每行包括一个被修复后的字符串。
输入例子1:
2
helloo
wooooooow
输出例子1:
hello
woow
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n=Integer.parseInt(sc.nextLine()); //不能用int n=sc.nextInt();输入里有数和字符
for(int i=0;i<n;i++){
StringBuffer s=new StringBuffer(sc.nextLine()); //涉及append()insert()delete()reverse()
for(int j=2;j<s.length();j++){ //3个3个判断,当j为s.length时判断最后三个
if(s.charAt(j)==s.charAt(j-1)&&s.charAt(j-1)==s.charAt(j-2)){ //不能三连等
s.deleteCharAt(j);
j--;
}else if(isPattern(s,j-3,j)){
s.deleteCharAt(j);
j--;
}
}
System.out.println(s);
}
sc.close();
}
public static boolean isPattern(StringBuffer str,int i,int j){
if(i<0)
return false;
return str.charAt(i)==str.charAt(i+1)&&str.charAt(j-1)==str.charAt(j);
}
}
请听题:给定N(可选作为埋伏点的建筑物数)、D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。
注意:
\1. 两个特工不能埋伏在同一地点
\2. 三个特工是等价的:即同样的位置组合(A, B, C) 只算一种埋伏方法,不能因“特工之间互换位置”而重复使用
输入描述:
第一行包含空格分隔的两个数字 N和D(1 ≤ N ≤ 1000000; 1 ≤ D ≤ 1000000)第二行包含N个建筑物的的位置,每个位置用一个整数(取值区间为[0, 1000000])表示,从小到大排列(将字节跳动大街看做一条数轴)
输出描述:
一个数字,表示不同埋伏方案的数量。结果可能溢出,请对 99997867 取模
输入例子1:
4 3
1 2 3 4
输出例子1:
4
例子说明1:
可选方案 (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)
输入例子2:
5 19
1 10 20 30 50
输出例子2:
1
例子说明2:
可选方案 (1, 10, 20)
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int d = sc.nextInt();
if (n < 3)
System.out.println(-1);
int[] array = new int[n];
for (int i = 0; i < n; i++)
array[i] = sc.nextInt();
long sum = 0;
int i = 0;
int j = 2;
while (j < n) {
if (array[j]-array[i] > d)
i++;
else if (j-i < 2)
j++;
else {
sum += process(j-i);
j++;
}
}
sum %= 99997867;
System.out.println(sum);
}
private static long process(long n) {
return n * (n-1) /2;
}
}
麻将
总共有36张牌,每张牌是1~9。每个数字4张牌。你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌14张牌中有2张相同数字的牌,称为雀头。除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。顺子的意思是递增的连续3个数字牌(例如234,567等),刻子的意思是相同数字的3个数字牌(例如111,777)
例如:
1 1 1 2 2 2 6 6 6 7 7 7 9 9 可以组成1,2,6,7的4个刻子和9的雀头,可以和牌
1 1 1 1 2 2 3 3 5 6 7 7 8 9 用1做雀头,组123,123,567,789的四个顺子,可以和牌
1 1 1 2 2 2 3 3 3 5 6 7 7 9 无论用1 2 3 7哪个做雀头,都无法组成和牌的条件。
现在,小包从36张牌中抽取了13张牌,他想知道在剩下的23张牌中,再取一张牌,取到哪几种数字牌可以和牌。
输入描述:
输入只有一行,包含13个数字,用空格分隔,每个数字在1~9之间,数据保证同种数字最多出现4次。
输出描述:
输出同样是一行,包含1个或以上的数字。代表他再取到哪些牌可以和牌。若满足条件的有多种牌,请按从小到大的顺序输出。若没有满足条件的牌,请输出一个数字0
输入例子1:
1 1 1 2 2 2 5 5 5 6 6 6 9
输出例子1:
9
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int[] num=new int[9];
for(int i=0;i<13;i++){
int tmp=sc.nextInt();
num[tmp-1]++;
}
int[] tmp=new int[9];
ArrayList<Integer> res=new ArrayList();
for(int i=1;i<=9;i++){
if(num[i-1]<4){
System.arraycopy(num,0,tmp,0,9);// public static void arraycopy(Object src,
// int srcPos, Object dest, int destPos, int length)
tmp[i-1]++;
if(canHu(tmp,14,false)){
res.add(i);
}
}
}
if(res.isEmpty()){
System.out.println(0);
}else{
for(int i=0;i<res.size();i++){
System.out.print(res.get(i)+" ");
}
}
}
public static boolean canHu(int[] num,int total,boolean head){
if(total==0){
return true;
}
if(!head){
for(int i=1;i<=9;i++){
if(num[i-1]>=2){
num[i-1] -=2;
if(canHu(num,total-2,true)){
return true;
}
num[i-1] +=2;
}
}
return false;
}else{
for(int i=1;i<=9;i++){
if(num[i-1]>0){
if(num[i-1]>=3){
num[i-1] -=3;
if(canHu(num,total-3,true)){
return true;
}
num[i-1] +=3;
}
if(i+2<=9 && num[i]>0 && num[i+1]>0){
num[i-1]--;
num[i]--;
num[i+1]--;
if(canHu(num,total-3,true)){
return true;
}
num[i-1]++;
num[i]++;
num[i+1]++;
}
}
}
return false;
}
}
}
小明是一名算法工程师,同时也是一名铲屎官。某天,他突发奇想,想从猫咪的视频里挖掘一些猫咪的运动信息。为了提取运动信息,他需要从视频的每一帧提取“猫咪特征”。一个猫咪特征是一个两维的vector<x, y>。如果x_1=x_2 and y_1=y_2,那么这俩是同一个特征。
因此,如果喵咪特征连续一致,可以认为喵咪在运动。也就是说,如果特征<a, b>在持续帧里出现,那么它将构成特征运动。比如,特征<a, b>在第2/3/4/7/8帧出现,那么该特征将形成两个特征运动2-3-4 和7-8。
现在,给定每一帧的特征,特征的数量可能不一样。小明期望能找到最长的特征运动。
输入描述:
第一行包含一个正整数N,代表测试用例的个数。每个测试用例的第一行包含一个正整数M,代表视频的帧数。接下来的M行,每行代表一帧。其中,第一个数字是该帧的特征个数,接下来的数字是在特征的取值;比如样例输入第三行里,2代表该帧有两个猫咪特征,<1,1>和<2,2>所有用例的输入特征总数和<100000N满足1≤N≤100000,M满足1≤M≤10000,一帧的特征个数满足 ≤ 10000。特征取值均为非负整数。
输出描述:
对每一个测试用例,输出特征运动的长度作为一行
输入例子1:
1
8
2 1 1 2 2
2 1 1 1 4
2 1 1 2 2
2 2 2 1 4
0
0
1 1 1
1 1 1
输出例子1:
3
例子说明1:
特征<1,1>在连续的帧中连续出现3次,相比其他特征连续出现的次数大,所以输出3
import java.util.Scanner;
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt(); //用例数
for (int i = 0; i < N; i++) { //一个用例一个用例处理
HashMap<String, Integer> map = new HashMap<>();
HashMap<String, Integer> tmp_map = new HashMap<>();
int M = sc.nextInt(); //帧数(行数)
int max = 1;
for (int j = 0; j < M; j++) { //一行一行处理
int c = sc.nextInt(); //特征数
tmp_map.clear();
for (int k = 0; k < c; k++) { //一个特征一个特征处理
int x = sc.nextInt();
int y = sc.nextInt();
String key = x + " " + y;
tmp_map.put(key, map.getOrDefault(key, 0) + 1); //根据上一行上保
//存的map查找是否上一行中有这一行重复的元素有则+1,无则为1
max = Math.max(tmp_map.get(key), max);
}
map.clear(); //因为只是考虑连续的,重复的会用+1的新值对覆盖,
map.putAll(tmp_map); //不重复的将用新的为1的值覆盖。
}
System.out.println(max);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UNKfpcNV-1595858199608)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\1589424279342.png)]
旅行
小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。
输入描述:
城市个数n(1<n≤20,包括北京)城市间的车票价钱 n行n列的矩阵 m[n][n]
输出描述:
最小车费花销 s
输入例子1:
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
输出例子1:
13
例子说明1:
共 4 个城市,城市 1 和城市 1 的车费为0,城市 1 和城市 2 之间的车费为 2,城市 1 和城市 3 之间的车费为 6,城市 1 和城市 4 之间的车费为 5,依次类推。假设任意两个城市之间均有单程票可购买,且票价在1000元以内,无需考虑极端情况。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int cityNum = in.nextInt();// 城市数目
int[][] dist = new int[cityNum][cityNum];// 距离矩阵,距离为欧式空间距离
for (int i = 0; i < dist.length; i++)
for (int j = 0; j < cityNum; j++) {
dist[i][j] = in.nextInt();
}
in.close();
int V = 1 << (cityNum - 1);// 对1进行左移n-1位,值刚好等于2^(n-1)
// dp表,n行,2^(n-1)列
int[][] dp = new int[cityNum][V];
// 初始化dp表第一列
for (int i = 0; i < cityNum; i++) dp[i][0] = dist[i][0];
//设想一个数组城市子集V[j],长度为V,且V[j] = j,对于V[j]即为压缩状态的城市集合
//从1到V-1 用二进制表示的话,刚好可以映射成除了0号城市外的剩余n-1个城市在不在子集V[j],1代表在,0代表不在
//若有总共有4个城市的话,除了第0号城市,对于1-3号城市
//111 = V-1 = 2^3 - 1 = 7 ,从高位到低位表示3到1号城市都在子集中
//而101 = 5 ,表示3,1号城市在子集中,而其他城市不在子集中
//这里j不仅是dp表的列坐标值,如上描述,j的二进制表示城市相应城市是否在子集中
for (int j = 1; j < V; j++)
for (int i = 0; i < cityNum; i++) { //这个i不仅代表城市号,还代表第i次迭代
dp[i][j] = Integer.MAX_VALUE; //为了方便求最小值,先将其设为最大值
if (((j >> (i - 1)) & 1) == 0) {
// 因为j就代表城市子集V[j],((j >> (i - 1))是把第i号城市取出来
//并位与上1,等于0,说明是从i号城市出发,经过城市子集V[j],回到起点0号城市
for (int k = 1; k < cityNum; k++) { // 这里要求经过子集V[j]里的城市回到0号城市的最小距离
if (((j >> (k - 1)) & 1) == 1) { //遍历城市子集V[j]
//设s=j ^ (1 << (k - 1))
//dp[k][j ^ (1 << (k - 1)),是将dp定位到,从k城市出发,经过城市子集V[s],回到0号城市所花费的最小距离
//怎么定位到城市子集V[s]呢,因为如果从k城市出发的,经过城市子集V[s]的话
//那么V[s]中肯定不包含k了,那么在j中把第k个城市置0就可以了,而j ^ (1 << (k - 1))的功能就是这个
dp[i][j] = Math.min(dp[i][j], dist[i][k] + dp[k][j ^ (1 << (k - 1))]); //^异或
//还有怎么保证dp[k][j ^ (1 << (k - 1))]的值已经得到了呢,
//注意所有的计算都是以dp表为准,从左往右从上往下的计算的,每次计算都用到左边列的数据
//而dp表是有初试值的,所以肯定能表格都能计算出来
}
}
}
}
System.out.println(dp[0][V - 1]);
}
}
计算连续重复key值
第一行包含一个正整数N,代表测试用例的个数。
每个测试用例的第一行包含一个正整数M,代表视频的帧数。
接下来的M行,每行代表一帧。其中,第一个数字是该帧的特征个数,接下来的数字是在特征的取值;比如样例输入第三行里,2代表该帧有两个猫咪特征,<1,1>和<2,2>
所有用例的输入特征总数和<100000
N满足1≤N≤100000,M满足1≤M≤10000,一帧的特征个数满足 ≤ 10000。
特征取值均为非负整数。
输入例子1:
1
8
2 1 1 2 2
2 1 1 1 4
2 1 1 2 2
2 2 2 1 4
0
0
1 1 1
1 1 1
输出例子1:
3
例子说明1:
特征<1,1>在连续的帧中连续出现3次,相比其他特征连续出现的次数大,所以输出
import java.util.Scanner;
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt(); //用例数
for (int i = 0; i < N; i++) { //一个用例一个用例处理
HashMap<String, Integer> map = new HashMap<>();
HashMap<String, Integer> tmp_map = new HashMap<>();
int M = sc.nextInt(); //帧数(行数)
int max = 1;
for (int j = 0; j < M; j++) { //一行一行处理
int c = sc.nextInt(); //特征数
tmp_map.clear();
for (int k = 0; k < c; k++) { //一个特征一个特征处理
int x = sc.nextInt();
int y = sc.nextInt();
String key = x + " " + y;
tmp_map.put(key, map.getOrDefault(key, 0) + 1); //根据上一行上保
//存的map查找是否上一行中有这一行重复的元素有则+1,无则为1
max = Math.max(tmp_map.get(key), max);
}
map.clear(); //因为只是考虑连续的,重复的会用+1的新值对覆盖,
map.putAll(tmp_map); //不重复的将用新的为1的值覆盖。
}
System.out.println(max);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mfwxlxPt-1595858199609)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\1591140340547.png)]
动态规划花销最少
给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。
输入描述:
城市个数n(1<n≤20,包括北京)
城市间的车票价钱 n行n列的矩阵 m[n][n]
输出描述:
最小车费花销 s
输入例子1:
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
输出例子1:
13
例子说明1:
共 4 个城市,城市 1 和城市 1 的车费为0,城市 1 和城市 2 之间的车费为 2,城市 1 和城市 3 之间的车费为 6,城市 1 和城市 4 之间的车费为 5,依次类推。假设任意两个城市之间均有单程票可购买,且票价在1000元以内,无需考虑极端情况。
动态规划
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[][] prices = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
prices[i][j] = in.nextInt();
}
}
int V = (int) Math.pow(2, n-1);
int[][] dp = new int[n][V];
// 计算回程价格
for (int i = 0; i < n; i++) {
dp[i][0] = prices[i][0];
}
// 计算各个子项,从上到下,从左到右
for (int j = 1; j < V; j++) {
// 处理一列
for (int i = 0; i < n; i++) {
// 初始化值
dp[i][j] = Integer.MAX_VALUE;
// 子集中不包含当前城市,即可以前往当前子集
// 通过 j 的二进制表示子集,当前位是1,表示对应城市在子集中
if ((j & (1 << (i-1))) == 0) {
// 轮询子集,由于是从城市 0 出发,所以只需要查看三个城市
for (int k = 1; k < n; k++) {
// 子集中有城市k
if ((j & (1 << (k-1))) > 0) {
// 从子集中排除城市k
int S = j ^ (1 << (k-1));
// 动态规划求最小值
dp[i][j] = Math.min(dp[i][j], (prices[i][k] + dp[k][S]));
}
}
}
}
}
System.out.println(dp[0][V-1]);
}
}
Z国的货币系统包含面值1元、4元、16元、64元共计4种硬币,以及面值1024元的纸币。现在小Y使用1024元的纸币购买了一件价值为n的商品,请问最少他会收到多少硬币?
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// 输入
Scanner scan = new Scanner(System.in);
while (scan.hasNext()){
// 计算需要组成多少钱
int num = 1024 - scan.nextInt();
// dp[i] 状态定义为找i元钱,需要的最少张数,从 0 - num 总共 num + 1种
int[] dp = new int[num + 1];
// 初始化dp数组,因为要找最小值,这里给每个位置赋最大值,即都是由1元组成的,即num/1
for (int i = 0; i < dp.length; i++) {
dp[i] = i;
}
// 定义钱的集合,方便遍历
int[] money = {1, 4, 16, 64};
// 状态转移方程 从 1 ~ num
for (int i = 1; i <= num ; i++) {
// dp[num]的最小值就是能组成它的前一步 + 1 和 本身进行比较
for (int j = 0; j < money.length; j++) {
if (i - money[j] >= 0){
dp[i] = Math.min(dp[i - money[j]] + 1, dp[i]);
}
}
}
System.out.println(dp[num]);
}
}
}
美团点评
2020校招系统开发方向笔试题
字符串和
以字符串的形式读入两个数字,再以字符串的形式输出两个数字的和。
输入例子1:
"-26"
"100"
输出例子1:
"74"
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
String str1=sc.nextLine();
String str2=sc.nextLine();
int num1=Integer.parseInt(getStr(str1,"\"")); //转义
int num2=Integer.parseInt(getStr(str2,"\""));
int sum=num1+num2;
String result=String.valueOf(sum);
System.out.println("\""+result+"\"");
}
public static String getStr(String str,String target){
int start=str.indexOf(target);
int end=str.lastIndexOf(target);
return str.substring(start+1,end);
}
}
回文
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串(回文串是一个正读和反读都一样的字符串)。
具有不同开始位置或结束位置的回文串,即使是由相同的字符组成,也会被计为是不同的子串。
输入例子1:
abc
输出例子1:
3
输入例子2:
aaa
输出例子2:
6
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
String str=sc.next();
int n=str.length();
int count=0;
for(int i=0;i<n;i++){ //遍历一个字符串的每一个小段
for(int j=i+1;j<=n;j++){
String res=str.substring(i,j);
if(suit(res)){
count++;
}
}
}
System.out.println(count);
}
public static boolean suit(String str){
boolean flag=false;
StringBuffer newStr=new StringBuffer(str);
newStr.reverse(); //必须把String转为StringBuffer才能reverse
String s=newStr.toString();
if(str.equals(s)){ //StringBuffer不能equals
flag=true;
}
return flag;
}
}
动态规划&合并金币最低成本
有 N 堆金币排成一排,第 i 堆中有 C[i] 块金币。每次合并都会将相邻的两堆金币合并为一堆,成本为这两堆金币块数之和。经过N-1次合并,最终将所有金币合并为一堆。请找出将金币合并为一堆的最低成本。
其中,1 <= N <= 30,1 <= C[i] <= 100
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] money = new int[n+1];
int[] preSum = new int[n+1];
for(int i = 1; i <= n; i++){
money[i] = sc.nextInt();
if(i == 1) preSum[i] = money[i];
else preSum[i] = preSum[i-1] + money[i];
}
sc.close();
int[][] dp = new int[n + 1][n + 1];
for(int len = 2; len <= n; len++){
for(int i = 1; i <= n - len + 1; i++){
int j = i + len - 1;
dp[i][j] = Integer.MAX_VALUE;
int sum = preSum[j] - preSum[i - 1];
for(int k = i; k < j; k++){
dp[i][j] = Math.min(dp[i][j],dp[i][k] + dp[k + 1][j] + sum);
}
}
}
System.out.println(dp[1][n]);
}
}
动态规划&最小前缀
给定一组个字符串,为每个字符串找出能够唯一识别该字符串的最小前缀
import java.util.Arrays;
import java.util.Scanner;
/**
* 字符串的最小公共前缀
* Created by Chris on 2020/7/19.
*/
public class Main {
private static String uniquePrefix(String str, String[] strArr){
//以第一个字母作为前缀
String prefix = str.substring(0, 1);
//遍历整个数组
for(String ele : strArr){
//当前字符串为本身:跳过
if(ele.equals(str)){
continue;
}
//当前字符串包含该前缀
while(ele.indexOf(prefix) == 0 && prefix.length() < str.length()){
prefix = str.substring(0, prefix.length() + 1);
}
}
return prefix;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//输入数据
int num = scanner.nextInt();
String[] strArr = new String[num];
for(int index = 0; index < num; index++){
strArr[index] = scanner.next();
}
//获取具有公共前缀的子字符串的最小公共前缀 + 后面一个字符
String[] res = new String[num];
for(int index = 0; index < num; index++){
res[index] = uniquePrefix(strArr[index], strArr);
System.out.println(res[index]);
}
}
}
2020校招后台开发方向笔试题
算布尔表达式的字符串
给出一个布尔表达式的字符串,比如:true or false and false,表达式只包含true,false,and和or,现在要对这个表达式进行布尔求值,计算结果为真时输出true、为假时输出false,不合法的表达时输出error(比如:true true)。表达式求值是注意and 的优先级比 or 要高,比如输入:true or false and false,等价于 true or (false and false),计算结果是输出 true。
将字符串分割后分别压栈,若遇到顶层为and时候进行弹出对比,最后保证栈中只有true、false、or字符串,再对栈中符号进行判断
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
String[] ss=scanner.nextLine().split(" ");
Stack<String> stack=new Stack<>();
for(int i=0;i<ss.length;i++){
String curr=ss[i];
//当前值为true或false时
if(curr.equals("true")||curr.equals("false")){
if(stack.isEmpty()){
stack.push(curr);
}else{
String top=stack.peek();
if(top.equals("true")||top.equals("false")){
System.out.println("error");
return;
}else{
if(top.equals("or")) stack.push(curr);
else{
stack.pop();
String pre=stack.pop();
if(curr.equals("false")||pre.equals("false")) stack.push("false");
else stack.push("true");
}
}
}
}
//当前值为and或or时
else{
if(stack.isEmpty()){
System.out.println("error");
return;
}else{
String top=stack.peek();
if(top.equals("and")||top.equals("or")){
System.out.println("error");
return;
}
stack.push(curr);
}
}
}
if(!stack.isEmpty()&&(stack.peek().equals("or")||stack.peek().equals("and"))){
System.out.println("error");
return;
}
while(!stack.isEmpty()){
String curr=stack.pop();
if(curr.equals("true")){
System.out.println("true");
break;
}
if(stack.isEmpty()) System.out.println("false");
}
}
正则匹配
给出两个字符串,分别是模式串P和目标串T,判断模式串和目标串是否匹配,匹配输出 1,不匹配输出 0。模式串中‘?’可以匹配目标串中的任何字符,模式串中的 ’*’可以匹配目标串中的任何长度的串,模式串的其它字符必须和目标串的字符匹配。例如P=a?b,T=acb,则P 和 T 匹配
import java.util.regex.Pattern;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String p = scanner.nextLine();
String t = scanner.nextLine();
if (Pattern.matches(p.replace("*",".*").replace("?","."),t)){
System.out.println(1);
}else {
System.out.println(0);
}
}
}
dfs
打车派单场景, 假定有N个订单, 待分配给N个司机。每个订单在匹配司机前,会对候选司机进行打分,打分的结果保存在N*N的矩阵A, 其中Aij 代表订单i司机j匹配的分值。
假定每个订单只能派给一位司机,司机只能分配到一个订单。求最终的派单结果,使得匹配的订单和司机的分值累加起来最大,并且所有订单得到分配。
import java.util.*;
public class Main {
static double[][] order;
static boolean[] visited;
static double max = -1;
static int[] ansPath;
static int N;
public static void main(String[] args) {
Scanner sc = new Scanner( System.in );
N = sc.nextInt();
order = new double[N][N];
for(int i = 0; i < N; i++){
for (int j = 0; j < N;j++)
order[i][j] = sc.nextDouble();
}
int[] path = new int[N];
visited = new boolean[N];
ansPath = new int[N];
dfs(0,path,0);
System.out.println(String.format( "%.2f",max ));
for(int i = 0; i < N; i++)
System.out.println((i+1) + " " + ansPath[i]);
}
public static void dfs(int curOrder, int[] path,double score){
if(curOrder < path.length){
for(int j = 0; j < N; j++){
if(!visited[j]){
path[curOrder] = j + 1;
visited[j] = true;
dfs(curOrder + 1, path, score + order[curOrder][j]);
visited[j] = false;
}
}
}else{
if(score > max){
max = score;
System.arraycopy( path,0,ansPath,0,path.length );
}
}
}
}
简单难度面试题
贪心
魔法货车
牛妹是鸡蛋商人。由于疫情严重,于是牛妹准备向疫情地区捐赠n个鸡蛋。牛妹请了m辆货车来运送这些鸡蛋,其中第i辆货车能运输x[i]个鸡蛋。因为预料到货车可能装不下所有的鸡蛋,于是牛妹请来了哈利波特·牛,哈利波特·牛使用一次魔法可以来让一辆货车的容量翻倍,牛妹想知道最少需要哈利波特·牛出手几次?
输入:
给定n,mn,m,xx数组
输出:
返回需要哈利波特·牛最少出手次数
示例1
输入
4,1,[2]
输出
1
说明
哈利波特·牛出手一次即可
备注:
一辆车可被多次施加魔法
import java.util.*;
public class Solution {
/**
*
* @param n int整型
* @param m int整型
* @param x int整型一维数组
* @return int整型
*/
public int Holy (int n, int m, int[] x) {
int l=x.length;
long sum=0;
int count=0;
for(int i=0;i<l;i++){
sum+=x[i];
}
long result=n-sum;
if(result<=0){
return 0;
}else{
Arrays.sort(x);
sum+=x[l-1];
x[l-1]*=2;
count++;
while(n>sum){
sum+=x[l-1];
x[l-1]*=2;
count++;
}
return count;
}
}
}
火柴拼图
题意:
牛妹有n根火柴,她想用这些火柴去拼正三角形或者正四边形。牛妹想让最后拼出的总面积尽可能大的,请你帮帮她。
这里没有考虑到火柴只剩下两个时虽然不3个,但可以把四个的正方形拆开使得其变成两个三角形,这里如果可以拆开的话肯定是一样的边长这样算下来拆开是不划算的。
输入:
给定n,Stick数组
Stick[i]表示第i根火柴的长度,一共有n根火柴
输出:
返回一个Vector,Vector中存有两个数字。
其中最大面积S=Vector[0]*\sqrt3/4+Vector[1]S=Vecto**r[0]∗3/4+Vecto**r[1]。
示例1
输入
4,[1,1,1,1]
输出
[0,1]
说明
构成一个边长为1的正四边形面积总和最大,值为1。所以Vector[0]=0,Vector[1]=1
import java.util.*;
public class Solution {
/**
*
* @param n int整型 n
* @param Stick int整型一维数组 Stick
* @return long长整型一维数组
*/
public long[] MaxArea (int n, int[] Stick) {
int max = 0;
for(int k:Stick){
max = Math.max(max,k);
}
long[]res = new long[2];
int[]a = new int[max+1];
for(int k:Stick){
a[k]++;
}
for(int i=max;i>=1;i--){
if(a[i]>=4){
int p = a[i]/4;
a[i] -= p*4;
res[1] += p*(long)i*i;
}
if(a[i]>=3){
res[0] += (long)i*i;
}
}
return res;
}
}
递增数组
题意:
牛牛有一个数组array,牛牛可以每次选择一个连续的区间,让区间的数都加1,他想知道把这个数组变为严格单调递增,最少需要操作多少次?
输入:
给定arrayarray数组
输出:
返回最小次数
示例1
输入
[1,2,1]
输出
2
说明
把第三个数字+2可以构成1,2,3
import java.util.*;
//因为我们要求得一个单调递增的序列,
//那么我们如果要选择一个区间的话这个区间必为后缀,
//不然我们选择一个中间的区间相当于把中间提高了,
//那么对后面就造成了不利的影响。
//所以我们只需要看每一个位置i与后面的一个元素i+1的差,
//位置i的贡献为max(0,array[i]+1-array[i+1])。
//时间复杂度O(n),空间复杂度O(n)。
public class Solution {
/**
*
* @param array int整型一维数组 array
* @return long长整型
*/
public long IncreasingArray (int[] array) {
long res = 0;
for(int i=0;i<array.length-1;i++){
res+=Math.max(0,array[i]+1-array[i+1]);
//前面的数字比后面的数字小1时为0,(x+1-y=0),前面的数字和后面的数字一样大时,(x+1-y=1)即要操作一次,前面的数字比后面的数字大时操作的次数为(x+1-y)
}
return res;
}
}
宝可梦攻击
牛牛是励志成为世界第一宝可梦大师的宝可梦训练家。现在他遇到了一个强劲的野生宝皮卡丘,野生宝皮卡丘的生命值是HP,攻击力是ACK,牛牛召唤的宝可梦是杰尼龟。杰尼龟的生命值是HP2,攻击力是ACK2,除此之外身为训练家还可以给宝可梦吃药让他满血复活(吃药发生在双方发动攻击之前,并且吃药一方不得在本回合发动攻击)。牛牛想知道他最少多少回合才能能打败野生宝皮卡丘?因为皮卡丘会电光一闪,所以皮卡丘总是比杰尼龟先发动攻击。如果牛牛无法战胜野生皮卡丘则返回-1。
输入:
包括HP ACK HP2 ACK2 1 \leq HP,ACK,HP2,ACK2 \leq 10^{12}1≤H**P,ACK,H**P2,ACK2≤1012
输出:
若能成功击败野生皮卡丘则返回最少回合数,否则返回-1。
示例1
输入
8,3,8,1
输出
14
说明
至少需要14回合战胜野生皮卡丘
示例2
输入
1,1,1,1
输出
-1
说明
皮卡丘先出招就直接打死了杰尼龟,所以无法获胜
import java.util.*;
public class Solution {
/**
*
* @param HP long长整型 HP
* @param ACK long长整型 ACK
* @param HP2 long长整型 HP2
* @param ACK2 long长整型 ACK2
* @return long长整型
*/
public long Pokemonfight (long HP, long ACK, long HP2, long ACK2) {
// write code here
// 被皮卡丘一次打死
if (ACK >= HP2) {
return -1;
}
// 把皮卡丘一次打死
if (ACK2 >= HP) {
return 1;
}
//皮卡丘两次打死牛牛,牛牛一次打不死皮卡丘,所以我每一回合都要一直加血,最终是赢不了怪兽
if ((ACK * 2 >= HP2)&&(HP>ACK2)) {
return -1;
}
long result = 0;
long killHP = (long)Math.ceil(HP * 1.0 / ACK2);//需要攻击皮卡丘的次数=皮卡丘生命值/牛牛攻击值(如果余数不等于0则加一次)
long killHP2 = (long)Math.ceil(HP2 * 1.0 / ACK);//需要攻击牛牛的次数=牛牛生命值/皮卡丘攻击值(余数不等于0则+1)
// 牛牛生命值还没有消耗完,皮卡丘就先被打死,此时不需要考虑吃药的情况
if (killHP < killHP2) {
return killHP;
}
// 牛牛每打killHP2 - 1回合就回血的回血次数
long times = (long)Math.ceil((killHP * 1.0 - killHP2 + 1) / (killHP2 - 2));
// 最后一轮,牛牛不需要回血,可以一直攻击怪兽,此时怪兽剩余血量
long remainHP = killHP - times * (killHP2 - 2);
// times表示回血次数,(killHP2 - 1)表示每次回血所经历的回合数,
s * (killHP2 - 1) + remainHP;
}
}
动态规划
求子串
给出一个字符串S,牛牛想知道这个字符串有多少个子序列等于"niuniu"
示例1
输入
"niuniniu"
输出
3
说明
删除第4,5个字符可以得到"niuniu"删除第5,6个字符可以得到"niuniu"删除第6,7个字符可以得到"niuniu"
import java.util.*;
public class Solution {
/**
* 好多牛牛
* @param s string字符串
* @return int整型
*/
public int solve (String s) {
// write code here
int[] dp=new int[6];//n ni niu niun niuni niuniu
int mod=1000000007;
for(int i=0;i<s.length();i++){
char c=s.charAt(i);
if(c=='n'){
dp[0]=(dp[0]+1)%mod;
dp[3]=(dp[2]+dp[3])%mod; // dp[2]+n=dp[3]
}else if(c=='i'){
dp[1]=(dp[1]+dp[0])%mod;
dp[4]=(dp[4]+dp[3])%mod; //dp[3]+i=dp[4]
}else if(c=='u'){
dp[2]=(dp[2]+dp[1])%mod; //dp[1]+u=dp[2]
dp[5]=(dp[5]+dp[4])%mod; //dp[4]+u=dp[5]
}
}
return dp[5];
}
}
简单变向
牛牛准备在一个3行n列的跑道上跑步。一开始牛牛位于(1,1)。
当牛牛位于第i行第j列时,他可以的下一步最多可能有三种选择:
跑道上有一些格子设置了路障(一个格子可能有多个路障),牛牛不能跑到路障上。现在牛牛想知道,从(1,1)到(3,n)有多少条不同的路径?
示例1
输入
4,1,[1],[2]
输出
2
说明
在第一行第二列的位置有一个障碍牛牛有两种跑法:1. (1,1)->(2,2)->(2,3)->(3,4)2. (1,1)->(2,2)->(3,3)->(3,4)
备注:
3\le n\le 1e5,1\le m\le 1e5, 1\le x_i\le3,1\le y_i\le n3≤n≤1e5,1≤m≤1e5,1≤xi≤3,1≤yi≤n,数据保证(1,1)和(3,n)上没有路障。第一个参数n代表列数第二个参数m代表路障个数第三个参数和第四个参数vector<int>x, y都包含m个元素,分别代表路障的行坐标和列坐标。同一个格子可能有多个路障。
import java.util.*;
public class Solution {
/**
* 简单变相
* @param n int整型
* @param m int整型
* @param x int整型一维数组
* @param y int整型一维数组
* @return int整型
*/
public int solve (int n, int m, int[] x, int[] y) {
// write code here
if(n==0)
return 0;
int[][] a=new int[3][n];
int mod = 1000000007;
for(int i=0;i<m;i++){
a[x[i]-1][y[i]-1]=1;
}
int[][] dp=new int[3][n];
dp[0][0]=1;
dp[1][0]=0;
dp[2][0]=0;
for(int i=1;i<n;i++){
if(a[0][i]!=1){
dp[0][i]=(dp[0][i-1]+dp[1][i-1])%mod;
}
if(a[1][i]!=1){
dp[1][i]=(dp[1][i-1]+dp[0][i-1])%mod;
dp[1][i]=(dp[1][i]+dp[2][i-1])%mod;
}
if(a[2][i]!=1){
dp[2][i]=(dp[2][i-1]+dp[1][i-1])%mod;
}
}
return dp[2][n-1];
}
}
复杂变向
牛牛准备在一个3行n列的跑道上跑步。一开始牛牛可以自己选择位于(1,1)还是(2,1)还是(3,1)。
跑道的每一格都有一些金币,当牛牛跑到一个格子,他会获得这个格子的所有金币。
\1. 不花费金币跑到第i行第j+1列2. 花费的金币跑到第i-1行第j+1列(如果i=1则不可以这么跑)。3. 花费的金币跑到第i+1行第j+1列(如果i=3则不可以这么跑)。(牛牛是一个富豪,本身带了很多的金币,所以你不用担心他钱不够用)
现在告诉你所有格子的金币数量和每一列的金币花费,牛牛想知道他跑到第n列最多可以赚得多少金币(赚得金币=获得金币-消耗金币)
示例1
输入
3,[1,9,3],[6,4,6],[1,1,5],[3,2,1]
输出
16
说明
一开始牛牛选择位于第2行第1列,拿到6个金币。然后牛牛花3金币到第1行的2列拿到9个金币,最后牛牛花费2金币到第2行第3列。总共获得21金币,消耗5金币。赚得16金币。
备注:
2\le n\le 1e5,0\le m_j,a_{i,j}\le1e42≤n≤1e5,0≤mj,ai,j≤1e4第1个参数n代表跑道的列数第2,3,4个参数vector<int> a1,a2,a3各有n个元素,代表第1,2,3行每一列的金币个数第5个参数vector<int> m有n个元素代表每一列进行换行的时候需要的金币花费
import java.util.*;
public class Solution {
/**
* 变相
* @param n int整型
* @param a1 int整型一维数组
* @param a2 int整型一维数组
* @param a3 int整型一维数组
* @param m int整型一维数组
* @return int整型
*/
public int solve (int n, int[] a1, int[] a2, int[] a3, int[] m) {
// write code her
int[] max = new int[3];
max[0] = a1[0];
max[1] = a2[0];
max[2] = a3[0];
for (int i = 1; i < n; i++) {
int max0 = max[0];
int max1 = max[1];
int max2 = max[2];
max[0] = max(max0, max1 - m[i - 1]) + a1[i];
max[1] = max(max1, max0 - m[i - 1], max2 - m[i - 1]) + a2[i];
max[2] = max(max2, max1 - m[i - 1]) + a3[i];
}
return max(max[0], max[1], max[2]);
}
private static int max(int a, int b) {
return Math.max(a, b);
}
private static int max(int a, int b, int c) {
return max(max(a, b), c);
}
}
吃蛋糕
第一天牛妹吃掉蛋糕总数三分之一(向下取整)多一个,第二天又将剩下的蛋糕吃掉三分之一(向下取整)多一个,以后每天吃掉前一天剩下的三分之一(向下取整)多一个,到第n天准备吃的时候只剩下一个蛋糕。
牛妹想知道第一天开始吃的时候蛋糕一共有多少呢?
示例1
输入
2
输出
3
import java.util.*;
public class Solution {
/**
* 前一天是x 后一天吃了1/3x+1 剩下2x/3-1
* @param n int整型 只剩下一只蛋糕的时候是在第n天发生的.
* @return int整型
*/
public int cakeNumber (int n) {
// write code here
if(n == 1)
return 1;
else
return 3 * (cakeNumber(n - 1) + 1) / 2;
}
}
收礼物
众所周知,牛妹有很多很多粉丝,粉丝送了很多很多礼物给牛妹,牛妹的礼物摆满了地板。
地板是N\times MN×M的格子,每个格子有且只有一个礼物,牛妹已知每个礼物的体积。
地板的坐标是左上角(1,1) 右下角(N, M)。
牛妹只想要从屋子左上角走到右下角,每次走一步,每步只能向下走一步或者向右走一步或者向右下走一步
每次走过一个格子,拿起(并且必须拿上)这个格子上的礼物。
牛妹想知道,她能走到最后拿起的所有礼物体积最小和是多少?
示例1
输入
[[1,2,3],[2,3,4]]
输出
7
说明
(1,1)->(1,2)->(2,3)
import java.util.*;
public class Solution {
/**
*
* @param presentVolumn int整型二维数组 N*M的矩阵,每个元素是这个地板砖上的礼物体积
* @return int整型
*/
public int selectPresent (int[][] presentVolumn) {
// write code here
if(presentVolumn.length == 0) return 0;
int row = presentVolumn.length;
int col = presentVolumn[0].length;
int[][] dp = new int[row][col];
dp[0][0] = presentVolumn[0][0];
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
if(i!=0 && j!=0){
dp[i][j] = presentVolumn[i][j] + Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1]);
}else if(i==0 && j!=0){
dp[i][j] = presentVolumn[i][j] + dp[i][j-1];
}else if(i!=0 && j==0){
dp[i][j] = presentVolumn[i][j] + dp[i-1][j];
}
}
}
return dp[row-1][col-1];
}
}
模拟
打字
牛妹在练习打字,现在按照时间顺序给出牛妹按下的键(以字符串形式给出,’<‘代表回退backspace,其余字符均是牛妹打的字符,字符只包含小写字母与’<’),牛妹想知道最后在屏幕上显示的文本内容是什么。
在文本内容为空的时候也可以按回退backspace(在这种情况下没有任何效果)。
输入:
给定一个字符串s,代表牛妹所按下的按键。
输出:
返回一个字符串代表最后在屏幕上显示的文本内容。
若为空则返回一个空串。
输入
"acv<"
输出
[复制](javascript:void(0)😉
"ac"
说明
牛妹在打完"acv"之后按了回退,所以最后是"ac"
import java.util.*;
public class Solution {
/**
*
* @param s string字符串
* @return string字符串
*/
public String Typing (String s) {
// write code here
char[] tmp = new char[s.length()];
int count = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c != '<') {
tmp[count++] = c;
} else {
count = count > 0 ? count - 1 : 0;
}
}
return new String(tmp).substring(0, count);
}
}
下象棋(问题)
牛妹在和牛牛下牛客象棋。现在轮到牛妹了,牛妹想知道她在这一回合能否战胜牛牛。
棋盘chessboard上只可能包含:炮,将,车,兵
牛客象棋的规则解释:
炮:炮在不吃子的时候,走动与车完全相同,但炮在吃棋子时,必须跳过一个棋子,我方的和敌方的都可以
兵:可以上下左右移动,每次只能移动一格
车:上下左右均可走,只要无棋子阻拦,步数不受限制。
将:可以上下左右移动,每次只能移动一格
接下来给出一个棋盘,牛妹的棋子用大写字母表示,牛牛的棋子用小写字母表示。
将用J,jJ,j表示,炮用P,pP,p表示,车用C,cC,c表示,兵用B,bB,b表示,没有棋子的格子用…表示
保证棋盘上一定同时包含JJ与jj各一个。
输入:
给定chessboard数组
输出:
牛妹能胜利则返回"Happy",否则返回"Sad"
示例1
输入
["......", "..B...", "P.C.j.", "......", "..b..."," ...J.." ]
输出
"Happy"
说明
牛妹的炮可以攻击到牛牛的将,所以获胜
import java.util.*;
public class Solution {
/**
*
* @param chessboard string字符串一维数组
* @return string字符串
*/
public String playchess (String[] chessboard) {
// write code here
int rSize = chessboard.length, cSize = chessboard[0].length();
int jR = 0, jC = 0;
/* 找到 j 所在的位置 */
for (int i = 0; i < rSize; i++) {
for (int j = 0; j < cSize; j++) {
if (chessboard[i].charAt(j) == 'j') {
jR = i;
jC = j;
break;
}
}
}
int[][] directions = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
/* 遍历四个方向 */
for (int[] direction : directions) {
int count = 0, nextR = jR + direction[0], nextC = jC + direction[1], length = 1;
while (isValid(nextR, nextC, rSize, cSize)) {
char current = chessboard[nextR].charAt(nextC);
if (((current == 'B' || current == 'J') && length == 1)
|| (current == 'P' && count == 1)
|| (current == 'C' && count == 0)) {
return "Happy";
}
count += current != '.' ? 1 : 0;
if (count > 1) {
break;
}
length++;
nextR = jR + direction[0] * length;
nextC = jC + direction[1] * length;
}
}
return "Sad";
}
private boolean isValid(int r, int c, int rSize, int cSize) {
return 0 <= r && r < rSize && 0 <= c && c < cSize;
}
}
思维
排队
牛牛和他的小伙伴一共n个人参加冬令营,他们的教官要求他们排成一排,每个人都有固定的位置,但是牛牛和他的小伙伴有些马虎,所以总是不记得他们到底站在哪个位置,但是他们记得之前站在他们左部分和站在他们右部分的人的人数差的绝对值a_i,根据这些值,牛牛想知道他们到底有多少种不同的站法?(结果需要对1e9+7取模)
示例1
输入
3,[2,0,2]
输出
2
说明
可能的站法(1 2 3), (3 2 1)
import java.util.*;
public class Solution {
/**
*
* @param n int整型
* @param a int整型一维数组
* @return int整型
*/
//如果n为奇数,中间人1种,其余人每人2种,但某人确定后,对应另一个位置的人也确定了,共2^((n-1)/2)种
//如果n为偶数,每人2种,但某人确定后,对应另一个位置的人也确定了,共2^(n/2)种
public int solve (int n, int[] a) {
// write code here
if(n==1) {
return a[0]==0 ? 1 : 0;
}
if(n==2) {
return a[0]==a[1]&&a[0]==1 ? 2 : 0;
}
Arrays.sort(a);
if(n%2!=0 && a[0]!=0) { //是奇数但是最小的差值不等于0是不成立
return 0;
}
int c = (n%2==0 ? 1 : 2);
for(int i=(n%2==0 ? 0 : 1)+1; i<n; i+=2) {
if(a[i-1]!=a[i] || a[i]!=c) {
return 0;
}
c+=2;
}
int result = 1;
for(int i=0;i<n/2;++i){
result = (result*2)%1000000007;
}
return result;
}
}
数学
车站建造
有108个村庄排在一条公路上,依次编号为0~108-1,相邻村庄距离为1,其中有n个村庄居住着牛牛,居住着牛牛的村庄从小到大依次为a0~an-1,其中保证a0=0.
现在需要建设车站,有两个要求必须被满足:
1、每个有牛牛居住的村庄必须修建车站。
2、相邻车站的距离必须为1或为某个质数。
现给出n和a数组,求需要建设车站的最小数量。
输入
3,[0,7,11]
输出
4
说明
在0,7,8,11处建造车站,差值分别为7,1,3,符合要求
备注:
输入数据包含一个数n和一个长度为n的数组a,数组a的下标为0~n-1,保证a数组递增,且a0=0。输出一个整数,表示答案。1<=n<=1000,a数组中数值保证小于10^8
import java.util.*;
public class Solution {
/**
*
* @param n int整型
* @param a int整型一维数组
* @return int整型
*/
public int work (int n, int[] a) {
//根据题意,车站数最少是n,
//如果相邻两个车站的距离不是1或素数,就需要在中间插入一个或多个车站来使每个车站之间的距离都是1或素数,
//也就是求把一个非素数分解为素数的个数,可以想到哥德巴赫猜想。
//对每个车站之间的距离进行判断,如果不是1或素数,就需要进行分解。
//如果距离是偶数,则可拆非为两个质数之和,意思就是在这两个村庄之间再修一座车站,ret++即可;
//如果是奇数,最多需要新增两个车站,将n拆分为n = (n - 2) + n,此时对于n-2,需要再分两种情况讨论:
//(i)n-2为质数,则n就是n-2和2两个质数之和,意思就是在这两个村庄之间再修一座车站,ret++即可;
//(ii)n-2为非质数,则可将n-2拆分成2个质数之和,进一步可将n拆分为3个质数之和,
// 意思就是在这两个村庄之间再修两座车站,ret+=2即可。
//但也有只需要新增一个车站的例子,比如9=2+7和15=2+13。
//如果想把一个奇数分解为两个素数,就必须是一个奇素数、一个偶素数,偶素数只有2,
//所以先判断一下-2之后能不能得到素数,如果能,只需要新增1个车站;
//如果不能,那么由哥德巴赫猜想,-2之后的奇数也一定能分解成2个素数,所以需要新增 2个车站。
int ans = n;
for (int i=1; i<n; i++) {
int temp = a[i]-a[i-1];
if (temp <= 3) continue;
else if (judge(temp)) continue;
else if (temp % 2==0) { //距离是偶数
ans += 1;
}
else { //距离是奇数
if (judge(temp-2)) ans+=1; //n-2为质数
else ans += 2; //n-2为非质数
}
}
return ans;
}
public boolean judge(int n) { //判断是否为质数
for (int i=2; i*i <= n; i++) {
if (n % i == 0)
return false;
}
return true;
}
}
牛牛的超市
牛牛最近在家闲的无聊,所以决定在家开一个小超市,为了方便卖东西,牛牛发明了一种用来兑换东西的新型货币,牛牛给这种新型货币起了个名字叫牛币,现在牛牛有n(n<=50)种不同的币值,其中币值为 value(value<=50) 的有 w(w<=20) 个,现在牛妹来到牛牛的超市买东西,牛妹有 x(x<=100) 元牛币,但是牛妹想将 x 元牛币换成若干零钱,请问有多少种换钱的方案?
输入
2,10,[[1, 5],[ 2, 4]]
输出
2
说明
10元可以由 2张1元的和4张2元的组成,也可以由4张1元的和3张2元的组成
import java.util.*;
public class Solution {
/**
*
* @param n int整型 :牛币值种类数
* @param x int整型 :牛妹拥有的钱数
* @param a int整型二维数组 :第二个vector中的第一列表示币值,第二列表示牛牛拥有币值的个数
* @return int整型
*/
public int solve (int n, int x, int[][] a) {
// write code here
//a[i][j]:面值i有j张
int dp[][] = new int[n+1][x+1];
dp[0][0] = 1;
for(int i=1;i<=a.length;i++){i 钱币种类数n
for(int j=0;j<=x;j++){//钱的总数
if(dp[i-1][j]!=0){
for(int k=0;k<=a[i-1][1]&&j+k*a[i-1][0]<=x;k++){//钱币的张数
dp[i][j+k*a[i-1][0]]+=dp[i-1][j];
}
}
}
}
return dp[n][x];
}
}
回路dfs
牛牛在一个迷宫中,迷宫有 n 个格子,有 m 条通道,每条通道连接两个格子 u, v,编号为 u 的格子与编号为 v的格子可互相到达,每人每条通道只能走一次。
牛牛想知道,他是否能从 11 号格子出发回到 11 号格子。
输入
第一行给定两个整数 n , m。
接下来m 行,每行有两个整数 u,v 。
输出
若能回到 11 号格子则返回Yes,否则返回No。
输入
[4, 4],[(1,2), (2, 3), (3,4),(4,1)]
输出
"Yes"
import java.util.*;
/*
* public class Point {
* int x;
* int y;
* }
*/
public class Solution {
/**
* 能回到1号点返回 Yes,否则返回 No
* @param param int整型一维数组 param[0] 为 n,param[1] 为 m
* @param edge Point类一维数组 Point.x , Point.y 分别为一条边的两个点
* @return string字符串
*/
public String solve (int[] param, Point[] edge) {
// write code here
// 寻找1号格子
int n=param[0];
int m=param[1];
if (m >= 100000) {
return "No";
}
int[] temp = new int[n];
int index = 0;
int num = 0;
for (Point p : edge) {
if (p.x == 1 || p.y == 1) {
temp[index] = p.x == 1 ? p.y : p.x;
index++;
num++;
}
}
if (num < 2) {
return "No";
}
int temIndex = ++index;
Point p = null;
for (int i = 0; i < temIndex; i++) { //遍历找到的 1号格子
for (int j = 0; j < m; j++) { //遍历m条管道
p = edge[j];
//如果p.x或p.y等于找到的1号点的temp中的值并且
if (p.x == temp[i] || p.y == temp[i] && j != num) {
if (p.x == temp[i] && !exists(p.y, temp)) {
temp[index++] = p.y;
} else if (!exists(p.x, temp)) {
temp[index++] = p.x;
}
if (temp[index - 1] == 1) {
return "Yes";
}
}
}
temIndex = index;
}
return "No";
}
public static boolean exists(int x,int [] arr) {
boolean result = false;
int size = arr.length;
for(int i = 0; i < size ; i++) {
if(arr[i] == x) {
result = true;
break;
}
}
return result;
}
}
函数
定义函数 f(x) = x^a + x^(a+1) +…+ x^(b-1) + x^b,然后在给定a和b的情况下,求f(x)%10000000033的值。
示例1
输入
1,3,2
输出
14
说明
f(2) = 2^1 + 2^2 + 2^3 = 14
备注:
其中0<=x,a,b<=1e9, 且 a<=b
import java.util.*;
public class Solution {
/**
*费马小定理:
* 费马小定理讲的是如果p是一个质数,而整数x不是p的倍数,则有x^(p-1)%p = 1;
* 这里10000000033是一个质数,而n-1显然不是其倍数,满足条件
* 两边同时除以x得到x^(p-2) % p = 1/x % p;
* 即(y/x)%p = y%p * (1/x)%p = y%p * x^(p-2)%p;
* (这里令p=10000000033, y = n^(b+1)-b^a, x=n-1)
* @param a int整型
* @param b int整型
* @param n int整型
* @return long长整型
*/
final static long MOD = 10000000033L;
public long solve (int a, int b, int n) {
//f(n) = (n^(b+1) - n^a)/(n-1)
// write code here
//n^a + n^(a+1) +...+ n^b= n^a(1 + n + n^2 + ...+ n^(b-a))=n^a(1 + n (1 + n...(1 +n)))
if (n == 0) {
return 0;
}
if (n == 1) {
return b - a + 1;
}
//等比数列求和公式 + 费马小定理
long ans = (quickPow(n, b + 1) - quickPow(n, a) + MOD) % MOD;
long inv = quickPow(n - 1, MOD - 2);
ans = multi(ans,inv);
return ans;
}
private long quickPow(long a, long n) {
long ans = 1;
while (n > 0) {
if ((n & 1) == 1) {
ans = multi(ans, a);
}
a = multi(a, a);
n >>= 1;//n = n>>1 右移一位
}
return ans;
}
private long multi(long a, long b) {
long ans = 0;
while (b > 0) {
if ((b & 1) == 1) {
ans += a;
ans %= MOD;
}
a = (a + a) % MOD;
b >>= 1;
}
return ans;
}
}
力扣
查找
数组查找
给定n个非负整数a1,a2,…,an,其中每个数字表示坐标(i, ai)处的一个点。以(i,ai)和(i,0)(i=1,2,3…n)为端点画出n条直线。你可以从中选择两条线与x轴一起构成一个容器,最大的容器能装多少水?
注意:你不能倾斜容器
例如:
输入 [1,8,6,2,5,4,8,3,7]
输出: 49
import java.util.*;
public class Solution {
/**
*
* @param height int整型一维数组
* @return int整型
*/
public int maxArea (int[] height) {
// write code here
//从两边向中间查找
int i = 0;
int j = height.length - 1;
int result = 0;
while (i < j) {
result = Math.max(Math.min(height[i], height[j]) * (j - i), result); //保证目前的最大值为result
if (height[i] < height[j]) { //放弃啊i点
i++;
} else { //放弃j点
j--;
}
}
return result;
}
}
数组中找出和为0三个数存入ArrayList
给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。
注意:
例如,给定的数组 S = {-1 0 1 2 -1 -4},↵↵ 解集为:↵ (-1, 0, 1)↵ (-1, -1, 2)
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
if (num == null) {
return result;
}
Arrays.sort(num);
int sum, left, right;
for (int i = 0; i < num.length - 2; i++) { //固定一个数,然后在该数字后面找剩下的两个数
//num[i] == num[i - 1] 则i-1计算过后i就跳过
if (i != 0 && num[i] == num[i - 1]) {
continue;
}
//固定一个数字,然后遍历它后面的数字一直到最后一个数字
left = i + 1;
right = num.length - 1;
/**
* 小于0时,把left往右边移动;
* 大于0时,把right往左边移动;
*/
while (left < right) {
sum = num[left] + num[right];
if (sum + num[i] == 0) {
ArrayList<Integer> solution = new ArrayList<>();
solution.add(num[i]);
solution.add(num[left]);
solution.add(num[right]);
result.add(solution);
left++;
right--;
//如果存在相同数字的情况则直接忽略
while (left < right && num[left] == num[left - 1]) {
left++;
}
while (left < right && num[right] == num[right + 1]) {
right--;
}
} else if (sum + num[i] < 0) { //三个数字的和不够大
left++;
} else { //三个数字的和超过了0
right--;
}
}
}
return result;
}
}
数组中到出和为目标值的四个数存入
给出一个有n个元素的数组S,S中是否有元素a,b,c和d满足a+b+c+d=目标值?找出数组S中所有满足条件的四元组。
注意:
例如:给出的数组 S = {1 0 -1 0 -2 2}, 目标值 = 0.↵↵ 给出的解集应该是:↵ (-1, 0, 0, 1)↵ (-2, -1, 1, 2)↵ (-2, 0, 0, 2)
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
ArrayList <ArrayList<Integer>> res=new ArrayList<>();
if(num==null||num.length==0){
return res;
}
Arrays.sort(num);
int n=num.length;
for(int i=0;i<n-3;i++){ //在第一个数到末三个数(留下进一步固定的数,left,right三个数字)中间固定一个数遍历这个数后面的数
if(i>0&&num[i]==num[i-1])continue; //重复的跳过
//固定的一个数加上就近的三个最小的数都大于目标值,后面的就更大了,num又是经过排序的,说明后面不可能出现符合要求的情况
if(num[i]+num[i+1]+num[i+2]+num[i+3]>target)break;
//num[i]和后面的最大的三个数相加小于目标值则说明不需要再对 i进行进一步判断,直接进入下一个循环
if(num[i]+num[n-1]+num[n-2]+num[n-3]<target) continue;
for(int j=i+1;j<n-2;j++){ //在i后面一个数和末二个数字(留下left,right两个数字)中间固定一个数
if(j-i>1&&num[j]==num[j-1])continue; //重复的跳过
//固定的两个数加上就近的最小的俩个数都大于目标值,则说明num[i]+num[j]这个组合以及比他们大的数不可能出现符合要求的情况,跳出j的整个for循环
if(num[i]+num[j]+num[j+1]+num[j+2]>target)break;
//num[i]+num[j]加上最大的两个数都小于目标值,num[j]不符合请求,跳过它进行下一个数字的判断
if(num[i]+num[j]+num[n-1]+num[n-2]<target) continue;
//遍历j后面的数字到最后一个数字
int left=j+1;
int right=n-1;
while(left<right){
int temp=num[i]+num[j]+num[left]+num[right];
if(temp==target){
ArrayList<Integer> solution = new ArrayList<>();
res.add(new ArrayList<>(Arrays.asList(num[i],num[j],num[left],num[right])));
while(left<right && num[left]==num[left+1])left++;
while(left<right && num[right]==num[right-1])right--;
left++;
right--;
}else if(temp<target){ //四个数的和不够大
left++;
}else{ //四个数的和太大了
right--;
}
}
}
}
return res;
}
}
数组中找出和最接近目标值的数的和
给出含有n个整数的数组s,找出s中和加起来的和最接近给定的目标值的三个整数。返回这三个整数的和。你可以假设每个输入都只有唯一解。
例如,给定的整数 S = {-1 2 1 -4}, 目标值 = 1.↵↵ 最接近目标值的和为 2. (-1 + 2 + 1 = 2).
import java.util.*;
public class Solution {
/**
*
* @param num int整型一维数组
* @param target int整型
* @return int整型
*/
public int threeSumClosest (int[] num, int target) {
int n=num.length;
if(n==3){
return (num[0]+num[1]+num[2]);
}
int sum=0;
int result=0;
int min=Integer.MAX_VALUE;
Arrays.sort(num);
for(int i=0;i<n-2;i++){
int left=i+1;
int right=n-1;
while(left<right){
sum=num[i]+num[left]+num[right];
if(sum>target){
right--;
}else{
left++;
}
if(Math.abs(sum-target)<min){
result=sum;
min=Math.abs(sum-target);
}
}
}
return result;
}
}
找数组中的第一个和最后一个目标值
给出一个有序数组,请在数组中找出目标值的起始位置和结束位置
你的算法的时间复杂度应该在O(log n)之内
如果数组中不存在目标,返回[-1, -1].
例如:
给出的数组是[5, 7, 7, 8, 8, 10],目标值是8,
返回[3, 4].
import java.util.*;
public class Solution {
/**
*
* @param A int整型一维数组
* @param target int整型
* @return int整型一维数组
*/
public int[] searchRange (int[] A, int target) {
// write code here
int low=0,high=A.length-1,mid=0;
int start= -1,end= -1;
while(low<=high){
mid=(low+high)/2;
if(A[mid]<target)low=mid+1;
else if(A[mid]>target)high=mid-1;
else{
start=end=mid;
while(start>=0&&A[start]==target)start--;//因为在8的时候 还是要进行这一步 所以start被移到了7的位置上 所以返回的时候要加1
while(end<A.length&&A[end]==target)end++;//因为在第二个8的时候 还是要进行这一步,所以start被移动到了10的位置上,所以返回的时候要减一
return new int[]{start+1,end-1};
}
}
return new int[]{start,end};//数组中不存在目标则
}
}
在有序数组中查找目标值
给出一个有序的数组和一个目标值,如果数组中存在该目标值,则返回该目标值的下标。如果数组中不存在该目标值,则返回如果将该目标值插入这个数组应该插入的位置的下标
假设数组中没有重复项。
下面给出几个样例:
[1,3,5,6], 5 → 2
[1,3,5,6], 2 → 1
[1,3,5,6], 7 → 4
[1,3,5,6], 0 → 0
import java.util.*;
public class Solution {
/**
*
* @param A int整型一维数组
* @param target int整型
* @return int整型
*/
public int searchInsert (int[] A, int target) {
// write code here
//遍历数组当它找到第一个大于等于目标值的数字时返回这个数的下标,找不到则返回A.length
int res=0;
for(int i=0;i<A.length;i++){
if(A[i]==target){
res=i;
}else if(target>A[A.length-1]){
res=A.length;
}else if(A[i]>target&&A[i+1]>target){
res=i+1;
}else{
continue;
}
}
return res;
}
}
判断数独局面是否符合规则
根据数独的规则Sudoku Puzzles - The Rules.判断给出的局面是不是一个符合规则的数独局面
数独盘面可以被部分填写,空的位置用字符’.’.表示
import java.util.*;
public class Solution {
public boolean isValidSudoku(char[][] board) {
for (int i = 0; i < 9; i++) {
Set<Character> row = new HashSet<Character>();
Set<Character> col = new HashSet<Character>();
Set<Character> cube = new HashSet<Character>();
for (int j = 0; j < 9; j++) {
// 第i行
if (board[i][j] != '.' && !row.add(board[i][j]))
return false;
// 第i列
if (board[j][i] != '.' && !col.add(board[j][i]))
return false;
//0,0 0,1 0,2 1,0 1,1 1,2 2,0 2,1 2,2
int cubeRow = 3 * (i / 3) + j / 3, cubeCol = 3 * (i % 3) + j % 3;
if (board[cubeRow][cubeCol] != '.' && !cube.add(board[cubeRow][cubeCol]))
return false;
}
}
return true;
}
}
填数独
public class Solution {
public void solveSudoku(char[][] board) {
if (board == null || board.length == 0 || board[0].length == 0)
return;
solve(board, 0);
}
public boolean solve(char[][] board, int num) {
if(num == 81)
return true;
int m = num / 9;
int n = num % 9;
// 如果该位已经有数字,则进行下次搜索
if(board[m][n] != '.'){
if(solve(board, ++num))
return true;
return false;
}
// 如果是‘.’,那么就试凑
for(char c= '1'; c <= '9'; c++){
if(!isValid(board, m, n, c))
continue;
board[m][n] = c;
if(solve(board, num+1))
return true;
// 试凑结果不对需要进行回溯
board[m][n] = '.';
}
return false;
}
public boolean isValid(char[][] board, int m, int n, char c) {
int tm = m / 3;
int tn = n / 3;
int mbegin = tm * 3;
int nbegin = tn * 3;
// 检查小的九宫格
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[mbegin + i][nbegin + j] == c)
return false;
}
}
// 检查行
for (int i = 0; i < 9; i++) {
if (board[m][i] == c)
return false;
}
// 检查列
for (int i = 0; i < 9; i++) {
if (board[i][n] == c)
return false;
}
return true;
}
}
递归
九键键盘
给出一个仅包含数字的字符串,给出所有可能的字母组合。
数字到字母的映射方式如下:(就像电话上数字和字母的映射一样)
Input:Digit string "23"Output:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
import java.util.*;
public class Solution {
/**
*
* @param digits string字符串
* @return string字符串ArrayList
*/
public ArrayList<String> letterCombinations (String digits) {
// write code here
HashMap<Character,String> map = new HashMap<>();
map.put('2',"abc");
map.put('3',"def");
map.put('4',"ghi");
map.put('5',"jkl");
map.put('6',"mno");
map.put('7',"pqrs");
map.put('8',"tuv");
map.put('9',"wxyz");
map.put('0',"0");
map.put('1',"1");
ArrayList<String> res = new ArrayList<>();
if(digits==null){
return res;
}
letterCombination(digits,0,"",map,res);
return res;
}
public void letterCombination(String digits, int start,String cur,HashMap<Character,String> map,ArrayList<String> res){
if(start==digits.length()){
res.add(cur);
return ;
}
//找到digits中第start个数字对应的字符串
String repl = map.get(digits.charAt(start));
//遍历找到的字符串
for(int i=0;i<repl.length();i++){
//找到digits中下一个数字对应的字符串,“”+repl的下标为i的数,即第i个数,因为0下标用“”补齐了,
letterCombination(digits,start+1,cur+repl.subSequence(i,i+1),map,res);
}
}
}
n个括号有几种写法
给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。
例如,给出n=3,解集为:
“((()))”, “(()())”, “(())()”, “()(())”, “()()()”
import java.util.*;
public class Solution {
/**
*
* @param n int整型
* @return string字符串ArrayList
*/
public ArrayList<String> generateParenthesis (int n) {
// write code here
ArrayList<String> list = new ArrayList<String>();
help(n,0,0,"",list);
return list;
}
public void help(int n, int x, int y, String s, ArrayList<String> list){
// 终止条件
if(y==n){
list.add(s);
}
if(x<n){
help(n,x+1,y,s+"(",list);
}
// 递归过程中 左括号x的个数必须大于等于右括号个数
if(x>y){
help(n,x,y+1,s+")",list);
}
}
}
排序
找两个数组的中位数
有两个大小分别为m和n的有序数组A和B。请找出这两个数组的中位数。你需要给出时间复杂度在O(log (m+n))以内的算法。
示例1
输入
[],[1]
输出
1.00000
import java.util.*;
public class Solution {
/**
*
* @param A int整型一维数组
* @param B int整型一维数组
* @return double浮点型
*/
public double findMedianSortedArrays (int[] A, int[] B) {
// write code here
int m = A.length,n = B.length;
int[] c = new int[m+n];
int i = 0,j = 0,k = 0;
for(;i < m && j < n && k < m+n;k++){
if(A[i] < B[j]) c[k] = A[i++];
else c[k] = B[j++];
}
while(k < m+n && j < n) c[k++] = B[j++];
while(k < m+n && i < m) c[k++] = A[i++];
if((m+n)%2 == 1) return (double)c[(m+n)/2];
else return (double)(c[(m+n)/2 - 1] + c[(m+n)/2]) / 2.0;
}
}
动态规划
字符串转换需要的最少步数
给定两个单词word1和word2,请计算将word1转换为word2至少需要多少步操作。
你可以对一个单词执行以下3种操作:
a)在单词中插入一个字符
b)删除单词中的一个字符
c)替换单词中的一个字符
输入
"ab","bc"
输出
2
/**
* 题目解析:
* 1. 本题涉及动态规划 - Dynamic Programming
* 之所以翻译成动态规划,是因为发明者是数学家,Programming不是CS中的编程,应该理解为规划或计划
* 该解释参考自算法设计与分析基础》
* 2. 给定两个单词word1和word2,需要将word1转换为word2
* 转换的条件是只能执行:插入一个字符,删除一个字符,替换一个字符
* 这三种操作,没执行一次,算一步,求word1转换为word2所需最小步数
*
* 解题思路:
* 1. 动态规划解题思路为: 拆分为子问题 - 找出递归式
* 2. 动态规划 需要用最优解来填充一张表 - 可能是一维表 or 二维表
*
* 问题举例理解: “ab” 到 “abc” 的最小步数
* 所有子问题:
* 0. "" 到 "a"【及 到 "ab" 及 到 "abc"】的最优解
* 1. "a" 到 "a" 【及 到 "ab" 及 到 "abc"】的最优解
* 2. "ab" 到 "a" 【及 到 "ab" 及 到 "abc"】的最优解
* 用这些最优解填充一张二维表 表的右下角为 整个问题的"ab" 到 "abc"的最优解
* 二维表:#代表空字符串
* 0 # a b c
* # 0 1 2 3
* a 1 0 1 2
* b 2 1 0 1
* 由表可知 由"ab" 到 "abc" 最优解:只需进行一步
*
* 递推公式:
* F(i,j) 代表word1的前i -1个字符组成的字符串到 word2的前j -1个字符组成的字符串的最优解
* 例:F(2, 3) 代表word1的前1个字符组成的字符串到 word2的前2个字符组成的字符串的最优解
* 若 i == j,则意为着不需额外操作,则F(i,j) 显然 等于 F(i - 1,j - 1)
* 若 i != j,则肯定需要1步操作来转换
* 以 "ab" 到 "abc"为例,该最优解为: min{"a" 到 "abc"的最优解, "ab" 到 "ab"的最优解,"a" 到 "ab" 的最优解 } + 1
* 所以 该情况递推公式为:F(i,j) = min{F(i - 1, j), F(i, j - 1),F(i - 1, j - 1) } + 1
*如果不等时取dp[i][j]的左,上,左上角的dp值的最小值+1
*/
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
// 第一行为 空字符串 到 对应字符串 所需 转换步数
for (int i = 0; i <= len2; i++) {
dp[0][i] = i;
}
// 第一列为 空字符串 到 对应字符串 所需 转换步数
for (int i = 0; i <= len1; i++) {
dp[i][0] = i;
}
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
// i 索引对应值== j 索引对应值,F(i, j ) = F(i - 1, j - 1)
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
// 取min{F(i - 1, j - 1) F(i - 1, j) F(i, j - 1) } + 1
int temp = Math.min(dp[i - 1][j] , dp[i][j - 1]);
dp[i][j] = Math.min(temp, dp[i - 1][j - 1]) + 1;//取三个数的最小值+1
}
}
}
return dp[len1][len2];
}
lp(n,0,0,"",list);
return list;
}
public void help(int n, int x, int y, String s, ArrayList list){
// 终止条件
if(y==n){
list.add(s);
}
if(x<n){
help(n,x+1,y,s+"(",list);
}
// 递归过程中 左括号x的个数必须大于等于右括号个数
if(x>y){
help(n,x,y+1,s+")",list);
}
}
}
## 排序
### 找两个数组的中位数
有两个大小分别为m和n的有序数组A和B。请找出这两个数组的中位数。你需要给出时间复杂度在O(log (m+n))以内的算法。
示例1
输入
[],[1]
输出
1.00000
```java
import java.util.*;
public class Solution {
/**
*
* @param A int整型一维数组
* @param B int整型一维数组
* @return double浮点型
*/
public double findMedianSortedArrays (int[] A, int[] B) {
// write code here
int m = A.length,n = B.length;
int[] c = new int[m+n];
int i = 0,j = 0,k = 0;
for(;i < m && j < n && k < m+n;k++){
if(A[i] < B[j]) c[k] = A[i++];
else c[k] = B[j++];
}
while(k < m+n && j < n) c[k++] = B[j++];
while(k < m+n && i < m) c[k++] = A[i++];
if((m+n)%2 == 1) return (double)c[(m+n)/2];
else return (double)(c[(m+n)/2 - 1] + c[(m+n)/2]) / 2.0;
}
}
动态规划
字符串转换需要的最少步数
给定两个单词word1和word2,请计算将word1转换为word2至少需要多少步操作。
你可以对一个单词执行以下3种操作:
a)在单词中插入一个字符
b)删除单词中的一个字符
c)替换单词中的一个字符
输入
"ab","bc"
输出
2
/**
* 题目解析:
* 1. 本题涉及动态规划 - Dynamic Programming
* 之所以翻译成动态规划,是因为发明者是数学家,Programming不是CS中的编程,应该理解为规划或计划
* 该解释参考自算法设计与分析基础》
* 2. 给定两个单词word1和word2,需要将word1转换为word2
* 转换的条件是只能执行:插入一个字符,删除一个字符,替换一个字符
* 这三种操作,没执行一次,算一步,求word1转换为word2所需最小步数
*
* 解题思路:
* 1. 动态规划解题思路为: 拆分为子问题 - 找出递归式
* 2. 动态规划 需要用最优解来填充一张表 - 可能是一维表 or 二维表
*
* 问题举例理解: “ab” 到 “abc” 的最小步数
* 所有子问题:
* 0. "" 到 "a"【及 到 "ab" 及 到 "abc"】的最优解
* 1. "a" 到 "a" 【及 到 "ab" 及 到 "abc"】的最优解
* 2. "ab" 到 "a" 【及 到 "ab" 及 到 "abc"】的最优解
* 用这些最优解填充一张二维表 表的右下角为 整个问题的"ab" 到 "abc"的最优解
* 二维表:#代表空字符串
* 0 # a b c
* # 0 1 2 3
* a 1 0 1 2
* b 2 1 0 1
* 由表可知 由"ab" 到 "abc" 最优解:只需进行一步
*
* 递推公式:
* F(i,j) 代表word1的前i -1个字符组成的字符串到 word2的前j -1个字符组成的字符串的最优解
* 例:F(2, 3) 代表word1的前1个字符组成的字符串到 word2的前2个字符组成的字符串的最优解
* 若 i == j,则意为着不需额外操作,则F(i,j) 显然 等于 F(i - 1,j - 1)
* 若 i != j,则肯定需要1步操作来转换
* 以 "ab" 到 "abc"为例,该最优解为: min{"a" 到 "abc"的最优解, "ab" 到 "ab"的最优解,"a" 到 "ab" 的最优解 } + 1
* 所以 该情况递推公式为:F(i,j) = min{F(i - 1, j), F(i, j - 1),F(i - 1, j - 1) } + 1
*如果不等时取dp[i][j]的左,上,左上角的dp值的最小值+1
*/
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
// 第一行为 空字符串 到 对应字符串 所需 转换步数
for (int i = 0; i <= len2; i++) {
dp[0][i] = i;
}
// 第一列为 空字符串 到 对应字符串 所需 转换步数
for (int i = 0; i <= len1; i++) {
dp[i][0] = i;
}
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
// i 索引对应值== j 索引对应值,F(i, j ) = F(i - 1, j - 1)
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
// 取min{F(i - 1, j - 1) F(i - 1, j) F(i, j - 1) } + 1
int temp = Math.min(dp[i - 1][j] , dp[i][j - 1]);
dp[i][j] = Math.min(temp, dp[i - 1][j - 1]) + 1;//取三个数的最小值+1
}
}
}
return dp[len1][len2];
}