目录
一、Map的应用篇
乒 乓球筐
简单的错误记录
二、动态规划篇
计算字符串的编辑距离
年终奖
最长不含重复字符的子字符串
合唱团
三、数组篇
顺时针打印矩阵
题目地址:乒乓球筐
小白代码
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNext()) { // 注意 while 处理多个 case
String A = in.next();
String B = in.next();
Map map = new HashMap<>();
for (int i = 0; i < A.length(); ++i) {
if (!map.containsKey(A.charAt(i))) {
map.put(A.charAt(i), 1);
}
else {
int temp = map.get(A.charAt(i));
map.put(A.charAt(i), temp + 1);
}
}
int[] arrB = new int[100];
for (int i = 0; i < B.length(); ++i) {
// System.out.println(B.charAt(i) - 'A');
arrB[B.charAt(i) - 'A']++;
}
int flag = 1;
for (int i = 0; i < B.length(); ++i) {
if (map.containsKey(B.charAt(i))) {
int j = B.charAt(i) - 'A';
if (arrB[j] <= map.get(B.charAt(i))) {
// 符合要求什么也不做
}
else {
// 不符合要求就改变flag
flag = 0;
}
}
else {
flag = 0;
}
}
if (flag == 1) {
System.out.println("Yes");
}
else {
System.out.println("No");
}
}
}
}
扩展做法
// write your code here
// write your code here
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNext()) { // 注意 while 处理多个 case
String temp = in.next();
StringBuilder A = new StringBuilder(temp);
String B = in.next();
char[] find = B.toCharArray();
int flag = 1;
for (char ch : find) {
// 不等于-1,说明该字符在A中
int index = A.indexOf(String.valueOf(ch));
if (index != -1) {
// 题目要求的是B中的字符在A中都有,并且在B中的数量不多于在A中的数量
// 我们就在遍历B中的过程,逐步的删除A中与B中相同的字符,如果从头到尾B中的字符都在A中
// 删除就是防止A中虽然有B中的字符,但没有B中的字符数量多
A.deleteCharAt(index);
}
else {
flag = 0;
}
}
if (flag == 1) {
System.out.println("Yes");
}
else {
System.out.println("No");
}
}
}
}
简单的错误记录
思路:
将每一个数据都存入到HashMap中,最后输出的时候只输出8个即可
代码如下
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Scanner;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
HashMap map = new LinkedHashMap();
String str = null;
while ((str = bf.readLine()) != null) {
String[] strs = str.split(" ");
String[] split = strs[0].split("\\\\"); //根据\分割
int LineNum = Integer.parseInt(strs[1]);
String FileName = split[split.length - 1];
//只保留最后16位
if (FileName.length() > 16)
FileName = FileName.substring(FileName.length() - 16);
String key = FileName + " " + LineNum;
//放入到HashMap中
int Number = 1;
if (map.containsKey(key))
map.put(key, map.get(key) + 1);
else {
map.put(key, Number);
}
}
int count = 0;
for (String string : map.keySet()) {
count++;
if (count > (map.keySet().size() - 8)) //输出最后八个记录
System.out.println(string + " " + map.get(string));
}
}
}
计算字符串的编辑距离
import java.io.*; // BufferedReader需要用 import java.util.*; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static void main(String[] args) throws IOException{ BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); String strA = null; // 注意 hasNext 和 hasNextLine 的区别 while ((strA = bf.readLine()) != null) { String strB = bf.readLine(); // f[i][j]表示把A字符串的前i个字符转换成字符串B的前j个字符,需要进行的编辑距离 int lenA = strA.length(); int lenB = strB.length(); int[][] ret = new int[lenA + 1][lenB + 1]; ret[0][0] = 0; for (int i = 0; i <= lenA; ++i) { ret[i][0] = i; } for (int j = 0; j <= lenB; ++j) { ret[0][j] = j; } // 状态转移方程:ret[i][j] = ret[i][j - 1] + 1 || ret[i][j] = ret[i - 1][j] + 1; /// ret[i][j] = ret[i - 1][j - 1] + 1; for (int i = 1; i <= lenA; ++i) { for (int j = 1; j <= lenB; ++j) { // 这里有些特殊,因为我们的字符真正的数据是从0,下标开始的——》即0下标和我们的ret[1][1]对应 // 但我们这个ret[0][0] if (strA.charAt(i - 1) == strB.charAt(j - 1)) { // 说明我们字符串A的第一个字符和我们字符串B的第一个字符相同ret[1][1] = 0; ret[i][j] = ret[i - 1][j - 1]; } else { ret[i][j] = Math.min(Math.min(ret[i - 1][j], ret[i][j - 1]), ret[i - 1][j - 1]) + 1; } } } System.out.println(ret[lenA][lenB]); } } }
题目链接:年终奖
import java.util.*; /** 这个计算过程中有三个特殊情况 情况1:如果是起点,那么直接忽略即可 情况2:如果处在第一行,那么由于是第一行,所以从起点走,它只可能是向右不断走得到的 情况3:如果处在第一列,那么由于是第一列,所以从起点走,它只可能是向下不断走的到的 */ public class Bonus { // 建立一个相同的矩阵board,board[i][j]表示从起点到i,j这一点中某条路径代表的礼物价值值总和 // 但要注意我们这里一开始传的参数board只是表示这一点的礼物价值,还不是到达这一点的路径总价值——》是经过不断构建才形成的礼物价值总和数组board[i][j]; public int getMost(int[][] board) { // write code here int row = board.length; // 行数 int col = board[0].length;// 每一行有多少列 // 先要把borad[0][j]第一行、board[i][0]第一列给构建好了,使得borad[i][0]、board[0][j]就代表从起点到这一列这个位置或者这一行这个位置的所获得的礼物总价值 for (int i = 1; i < row; ++i) { board[i][0] = board[i - 1][0] + board[i][0]; } for (int j = 1; j < col; ++j) { board[0][j] = board[0][j - 1] + board[0][j]; } // 此时,第一行borad[i][0]、board[0][j]就代表从起点到这一列或者这一行的总价值 // 接下来我们要构建中间的了(在第一行和第一列的基础上) for (int i = 1; i < row; ++i) { for (int j = 1; j < col; ++j) { // 对于这样一个棋盘,到达(i,j)这一点要么是上一行(i-1,j)向下移动一行得到,要么是前一列(i,j-1)向右移动一列得到 board[i][j] = Math.max(board[i - 1][j], board[i][j - 1]) + board[i][j]; // board[i][j]代表了此时从起点到board[i][j]位置所经过路径所获得的礼物总价值 } } return board[row - 1][col - 1]; } }
如果你对上面的board[i][j]代表的究竟是这一点的礼物价值,还是从起点到这一点所经过的路径的总价值不太清楚的话。
我们不妨另外构建一个矩阵gifts[i][j]来专门表示从起点到i,j
这一点中某条路径代表的礼物价值值总和。
思路分析:
这道题属于贪心算法,但本质仍然是动态规划。对于这样一个棋盘,到达(i,j)
这一点要么是上一行(i-1,j)
向下移动一行得到,要么是前一列(i,j-1)
向右移动一列得到
这个计算过程中有三个特殊情况
其余情况均是即可向右走,也可以向下走
代码:
import java.util.*; public class Bonus { public int getMost(int[][] board) { // write code here int row = board.length; int col = board[0].length; // gifts[i][j]表示从起点到i,j这一点中某条路径代表的礼物价值值总和 int[][] gifts = new int[row][col]; gifts[0][0] = board[0][0]; for (int i = 1; i < row; ++i) { gifts[i][0] = gifts[i - 1][0] + board[i][0]; } for (int j = 1; j < col; ++j) { gifts[0][j] = gifts[0][j - 1] + board[0][j]; } for (int i = 1; i < row; ++i) { for (int j = 1; j < col; ++j) { // 对于这样一个棋盘,到达(i,j)这一点要么是上一行(i-1,j)向下移动一行得到,要么是前一列(i,j-1)向右移动一列得到 gifts[i][j] = Math.max(gifts[i - 1][j], gifts[i][j - 1]) + board[i][j]; // board[i][j]代表此点上的礼物价值 // gifts[i][j]代表从起点到i,j这一点所经过路径代表的礼物价值总和 } } return gifts[row - 1][col - 1]; } }
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param s string字符串 * @return int整型 */ public int lengthOfLongestSubstring (String str) { // write code here int[] dp= new int[str.length()]; char[] s = str.toCharArray(); dp[0] = 1; // dp[i]代表以s[i]结尾的的字符串的最长子字符串的长度 Map
map = new HashMap<>(); map.put(str.charAt(0), 0); int res = 1, len = 0; // 当字符串只有一个字符,程序不会执行for循环,此时要进行特判 for (int i = 1; i < str.length(); ++i) { if (!map.containsKey(str.charAt(i))) { dp[i] = dp[i - 1] + 1; } else { dp[i] = Math.min(dp[i - 1] + 1, i - map.get(str.charAt(i))); // dp[i] = i - map.get(str.charAt(i)); } map.put(str.charAt(i), i); res = Math.max(dp[i], res); } return res; } }
牛客链接
import java.util.*; import java.io.*; public class Main { public static void main(String[] args) throws IOException{ BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); String str = null; while((str = bf.readLine()) != null) { int n = Integer.parseInt(str); String num = bf.readLine(); String[] nums = num.split(" "); int[] a = new int[nums.length]; for (int i = 0; i < nums.length; ++i) { a[i] = Integer.parseInt(nums[i]); } String str2 = bf.readLine(); String[] strs2 = str2.split(" "); int k = Integer.parseInt(strs2[0]); int d = Integer.parseInt(strs2[1]); long[][] maxVal = new long[n + 1][k + 1]; long[][] minVal = new long[n + 1][k + 1]; // maxVal[i][j]代表当最后一个是第i个同学,共选了j个同学所得到了最大能力值 // 初始化 for (int i = 1; i <= n; ++i) { maxVal[i][1] = minVal[i][1] = a[i - 1]; } long ret = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= k; ++j) { // 约束条件 for (int m = i - 1; m >= Math.max(i - d, 1); --m) { maxVal[i][j] = Math.max(maxVal[i][j], Math.max(maxVal[m][j - 1] * a[i - 1], minVal[m][j - 1] * a[i - 1])); minVal[i][j] = Math.min(minVal[i][j], Math.min(minVal[m][j - 1] * a[i - 1], maxVal[m][j - 1] * a[i - 1])); } } ret = Math.max(ret, maxVal[i][k]); } System.out.println(ret); } } }
解题思路
1、声明四个变量,分别为左界限 L,右界限 R,上界限 T,下界限 B。
2、先从左往右遍历,然后从上到下,从右往左,最后从下往上。循环这个操作,每次从左往右遍历完,T += 1,说明最上面这一行已经遍历过了,上界限 T 应该往下移了;从上往下遍历完, R -= 1,说明最右边这一列已经遍历过了,右界限 R 应该往左移了;其他遍历操作也是如此。当四个界限中,T 和 B,或者 L 和 R 碰撞在一起,说明遍历完成,退出循环。
3、值得注意的是,需要考虑非方阵的情况,比如矩阵 A[3][4],因为行数比列数少,T 和 B 碰撞时 L 和 R 仍未碰撞,若此时还在循环体内,会继续执行遍历操作。所以在循环体内,需要时刻监控界限碰撞的情况。
import java.util.ArrayList; public class Solution { public ArrayList
printMatrix(int [][] matrix) { int t = 0; int b = matrix.length - 1, row = matrix.length; int l = 0; int r = matrix[0].length - 1, col = matrix[0].length; ArrayList list = new ArrayList<>(); while (t <= b && l <= r) { // 从左到右, 需要注意我们的边界都是动态变化的 for (int i = l; i <= r; ++i) { list.add(matrix[t][i]); } ++t; // 第一行遍历完了,上边界要发生变化 if (t > b || l > r) break; // 从上到下 for (int j = t; j <= b; ++j) { list.add(matrix[j][r]); } --r; if (t > b || l > r) break; // 从右到左 for (int i = r; i >= l; --i) { list.add(matrix[b][i]); } --b; if (t > b || l > r) break; // 从下到上 for (int j = b; j >= t; --j) { list.add(matrix[j][l]); } ++l; } return list; } }