Java题目训练——汽水瓶和查找两个字符串a,b中的最长公共子串

目录

一、汽水瓶

二、查找两个字符串a,b中的最长公共子串


一、汽水瓶

题目描述:

某商店规定:三个空汽水瓶可以换一瓶汽水,允许向老板借空汽水瓶(但是必须要归还)。 小张手上有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(n^{3}),空间复杂度: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));
    }

}

如有疑问或想法,欢迎一起讨论学习~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值