目录
一、汽水瓶
题目描述:
某商店规定:三个空汽水瓶可以换一瓶汽水,允许向老板借空汽水瓶(但是必须要归还)。 小张手上有n个空汽水瓶,她想知道自己最多可以喝到多少瓶汽水。
数据范围:输入的正整数满足1<=n<=100
注意:本题存在多组输入。输入的 0 表示输入结束,并不用输出结果。
输入描述:
输入文件最多包含 10 组测试数据,每个数据占一行,仅包含一个正整数 n( 1<=n<=100 ),表示小张手上的空汽水瓶数。n=0 表示输入结束,你的程序不应 当处理这一行。
输出描述:
对于每组测试数据,输出一行,表示最多可以喝的汽水瓶数。如果一瓶也喝不到,输出0。
示例
示例1
输入:3
10
81
0
输出:1
5
40
说明:样例 1 解释:用三个空瓶换一瓶汽水,剩一个空瓶无法继续交换 样例 2 解释:用九个空瓶换三瓶汽水,剩四个空瓶再用三个空瓶换一瓶汽水,剩两个空瓶,向老板 借一个空瓶再用三个空瓶换一瓶汽水喝完得一个空瓶还给老板
题目解析:
通过循环遍历模拟兑换汽水瓶的过程,一开始一共有n瓶,设置一个变量count存储总共能换多少瓶,n / 3 得到这次能换多少瓶,n = n % 3 得到换完后剩下不够换的瓶数,再加上换到的瓶数等于下次去换的瓶数,这样遍历循环,直到n总数不够换,需要注意特殊情况就是如果n == 2,这时可以向商店老板再借一个瓶子,然后换到后再换给他,此时还能再换一次。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextInt()){
int n = scanner.nextInt();
if(n == 0){
break;
}
System.out.println(num(n));
}
}
public static int num(int n){
int count = 0;
while(n >= 3){
//能换多少瓶
int t = n / 3;
count += t;
//不够换的瓶数
n %= 3;
//换好后的总瓶数
n += t;
//特殊情况
if(n == 2){
//借一瓶
n++;
}
}
return count;
}
}
二、查找两个字符串a,b中的最长公共子串
题目描述:
查找两个字符串a,b中的最长公共子串。若有多个,输出在较短串中最先出现的那个。 注:子串的定义:将一个字符串删去前缀和后缀(也可以不删)形成的字符串。请和“子序列”的概念分开!
数据范围:字符串长度1<=length<=300
进阶:时间复杂度:O(
),空间复杂度:O(n)
输入描述:
输入两个字符串
输出描述:
返回重复出现的字符
示例
示例1
输入:abcdefghijklmnop
abcsafjklmnopqrstuvw
输出:jklmnop
说明:
题目解析:
这里介绍三种思路:
因为题目要求:若有多个,输出在较短串中最先出现的那个,所以我们在寻找最长子串时需要先判断一下两个字符串的长度,再确定传参位置(确保短字符串在外层循环)。
1. 动态规划(推荐)
(1). 创立状态数组arr = new int[s1.length() + 1][s2.length() + 1]。
(2). 寻找状态方程:如果此时位置的字符相同(s1.charAt(i - 1) == s2.charAt(j - 1)),那么说明这时字符是重复的,直接等于上一个状态的结果再+1。
(3). 设立初始态:默认为0即可,本题通过条件自动更新。
由于题目要求返回最长子串,所以要设立一个变量max存储最大长度,设立一个start存储最长子串的起始下标,最后返回s1.substring(start, start + max)即为最长子串。
2. 普通思路
设置一个结果字符串res,然后通过两层循环,外层循环控制子串的起始位置,新建一个StringBuilder的字符串sb(方便修改),内层循还向后遍历,将字符通过StringBuilder的append方法添加至sb,如果此时的字符串sb被字符串s2所包含,那么才能判断sb的长度(子串)是否大于保存的结果字符串res的长度,遍历完所有子串的情况,最后返结果字符串res即为最长子串。
这里需要注意循环的条件和范围,范围都是对s1的限制。
3. 左右指针——可以看作普通思路的优化
设置左指针left从s1的左端开始,设置右指针right从s1的右端开始,思路有点像常规思路,外循环控制左指针的位置,内循环只要left <= right,就判断此时截取的s1.substring(left, right)是否被s2包含,并且长度有没有超过之前记录的最长子串的长度max,如果没有超过或者不符合条件,rright--,继续遍历;只要超过,就是这种情况(left = i)的最大长度(因为right一直在--),直接退出内循环(继续遍历也符合s2包含的条件,但长度越来越短,此时已经找到最长子串,没有必要继续)。
由于题目要求返回最长子串,所以要设立一个变量max存储最大长度,设立一个start存储最长子串的起始下标,最后返回s1.substring(start, start + max)即为最长子串。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s1 = scanner.nextLine();
String s2 = scanner.nextLine();
if(s1.length() <= s2.length()){
fun1(s1, s2);
}else {
fun1(s2, s1);
}
}
//1. 动态规划
public static void fun(String s1, String s2){
int[][] arr = new int[s1.length() + 1][s2.length() + 1];
arr[1][1] = 0;
int max = 0;
int start = 0;
for (int i = 1; i <= s1.length(); i++) {
for (int j = 1; j <= s2.length(); j++) {
if(s1.charAt(i - 1) == s2.charAt(j - 1)){
arr[i][j] = arr[i - 1][j - 1] + 1;
if(max < arr[i][j]){
max = arr[i][j];
start = i - max;
}
}
}
}
System.out.println(s1.substring(start, start + max));
}
//2. 常规思路
public static void fun1(String s1, String s2){
String res = "";
for (int i = 0; i < s1.length(); i++) {
StringBuilder sb = new StringBuilder();
for(int j = i; j < s1.length(); j++){
if(s2.contains(sb.append(s1.charAt(j)))){
if(sb.length() > res.length()){
res = sb.toString();
}
}
}
}
System.out.println(res);
}
//3. 左右指针
public static void fun2(String s1, String s2){
int max = 0;
int start = 0;
for (int i = 0; i < s1.length(); i++) {
int left = i;
int right = s1.length();
while(left <= right){
int t = s1.substring(left, right).length();
if(s2.contains(s1.substring(left, right)) && max < t){
max = t;
start = left;
break;
}
right--;
}
}
System.out.println(s1.substring(start, start + max));
}
}
如有疑问或想法,欢迎一起讨论学习~