Java 100道经典机试笔试题(02)——附可运行代码

导语

每篇将有两道经典Java机试题,每道题后面均为大家附上代码,每一道题目力求:

  • 能够在JDK11环境下编译
  • 在Eclipse JavaIDE中运行通过
  • 思路易想易懂易学
  • 重点代码有注释

 第003题    最大前缀(难度:★★☆☆☆)

题目描述:

求一个字符串的最大回文前缀长度。回文是指正反方向读起来都一样的字符串,比如“abcdcba”就是一个回文。"10010"中最大回文是“1001”,长度为4,所以输出4。一个字符串,至少包含一个字节。每个字节是一个字符。最大长度可能有几十万字节。 

输入示例:

asdfdsa

1010

10011

10101001

apple

输出示例:

7

3

4

5

1

 思路

1.利用String.charAt()方法分别从一头(start)一尾(end)开始比较。

2.当start != end时,且之前从未配对成功过,则end前移一位,start指向0,重复1;若之前已有配对成功,则只需start指向0,end不动,重复1。

3.当start == end 时,start后移一位,end前移一位。

 代码

import java.util.Scanner;

public class Test{
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String string = sc.nextLine();               //读取一行字符串
		int start = 0, end = string.length() - 1;    //start指向第一个位置,end指向最后一个位置
		int count = end;                             //计数
		boolean flag = false;                        //初始标记未曾有对应位相同
		//跳出循环条件:start所指位置序号>end所指位置序号
		while(start - end <0) {
			if(string.charAt(start) == string.charAt(end)) {
				flag = true;                         //已有对应位相同
				start++;                             //start向后移一位
				end--;                               //end向前移一位
			}
			else {
				if(!flag) {
					end--;                           //从未出现对应位相同时,才需end--
				}
				start = 0;                           //start重新指向第一位
				count = end;                         //count指向现在的end位置
				flag = false;                        //标记重置为false
			}
		}
		System.out.println(count + 1);               //count所指的序号+1就是最长前缀长度
	}
}

运行结果

         


第004题    最短昵称(难度:★★★★☆)

题目描述:

       小南瓜要注册一个昵称,为了体现自己昵称的独特性,小南瓜想使自己的昵称包含指定的 字符串,但是昵称注册有长度限制, 为此小南瓜要知道包含自己指定字符串最短的昵称长度是多少。第一行输入一个整数n,代表要包含的字符串个数,之后紧跟n行,输入要包含的字符串, 输出最短字符串长度。

输出示例:

3

ABC

CBA

AB

输出示例:

5

思路

本题稍微有点复杂,需要费点篇幅来详细说一下。

举个例子:现有三个字符串a:ACAB、b:ABA、c:CA。那么按照题意,我们要找到一个字符串,里面包含a、b 、c子串,而且这个字符串要最短。

直观想:要包含这三个子字符串,直接把他们连在一起,肯定能包含 :ACABABACA。但很显然,这个不是最短的,略加思索便可得知:ACABA才是最短的,里面既饱含a(ACABA), 又包含b(ACABA)、还包含c(ACABA)。

既然已经明白了何谓“最短”,那么我们该如何找到这个最短的字符串呢?首先最容易想到的,如果一个字符串中直接包含了另外一个字符串,那么直接可以不用考虑所包含的字符串了,如a和c的关系ACAB中包含着CA,我们很容易想到Java中使用String.contains()方法来判断长字符串中是否包含短字符串。要判断两两之间是否有包含关系,可以使用循环,但是显然,只能是长字符串包含短字符串。所以,在比较之前可以先对字符串按照长度先排序,按照从短到长排序,这样只需判断排在后面的字符串是否包含排在前面的字符串了。由此又自然而然想到,判断完,应该给予已包含的字符串一个标记true,告诉程序,这个字符串已经包含在其它字符串里了,无需再次考虑。所以字符串初始化均标记成false。

数据结构声明如下:

class Nickname{
	private String string;                 //保存字符串
	private boolean flag;                  //标记
	
	public Nickname() {                    //构造函数
		this.string = "";
		this.flag = false;             //flag初始化为false,代表该nickname不包含于任何字符串
	}
	//setter、getter方法……
}

其次,我们再考虑:除了已经包含的字符串,没有被包含的字符串如何处理?观察a(ACAB)、b(ABA), 很容易发现,因为a的尾部包含“AB”,b的头部包含“AB”,才可能使得这两个字符串存在最短的可能,b连在a的后面,合并在一起得到ACABA。我们再考虑,a的头部有“A”,b的尾部也有“A”,那么可不可以把a连在b后面呢?即ABACAB,这样起码也比直接连接ABAACAB要短。当然可以,只不过,题目要求最短,所以选择前者连接方式。

好了,到现在,我们知道了,存在最短连接的两个字符串一定是如下两种情况:

1).短的尾部长的头部有相同部分    如:ABA(短)、BAAC(长),最短为ABAAC
2).长的尾部短的头部有相同部分    如:ACAB(长)、ABA(短),最短为ACABA

如果短的尾部和长的头部长的尾部和短的头部同时有相同部分,该选择哪一种连接呢?显然,我们要选择含有相同部分最多的那一种连接方式,正如我们举的例子a(ACAB)、b(ABA),因为a尾部和b的头部有2个(“AB”)相同的部分,a的头部和b的头部只有1个(“A”)相同部分,既然要求最短,那么我们当然希望,包含相同越多越好。所以对于两个字符串按照1)和2)分别比较,并分别添加一个计数器count_i和count_j:

//若果count_i > count_j,则将短字符串剪切到长字符串后面
if(count_i > count_j) {
	longString = longString + shortString.substring(count_i, shortString.length());
}
//若果count_i <= count_j,则将长字符串剪切到短字符串后面
if(count_i <= count_j) {
	longString = shortString.substring(0, shortString.length() - count_j) + longString;
}

将新拼接的字符串存入longString中,并将shortString的标记设置为true,重新排序。

//新得到的字符串放到长字符串nickname[y]中,同时将短字符串nickname[x]的标记设置为true
nickname[y].setString(longString);
nickname[x].setFlag(true);
//重新排序,以保证新的字符串仍是按长度排好序
change(nickname, n);

 

代码

import java.util.Scanner;

class Nickname{
	private String string;
	private boolean flag;
	
	public Nickname() {                    //构造函数
		this.string = "";
		this.flag = false;                 //flag初始化为false,代表该nickname不包含于任何字符串
	}
	public void setString(String string) { //setter、getter方法
		this.string = string;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public String getString() {
		return string;
	}
	public boolean getFlag() {
		return flag;
	}
}

public class Test{
	public static Nickname[] change(Nickname nickname[],int n) {
		//此处两个循环:将输入的字符串按照长度从短到长排序
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n - 1; j++) {
				if(nickname[j].getString().length() > nickname[j + 1].getString().length()) {
					//nickname的两个成员均要交换
					String temp_string = nickname[j + 1].getString();
					boolean temp_bool = nickname[j + 1].getFlag();
					
					nickname[j + 1].setString(nickname[j].getString());
					nickname[j + 1].setFlag(nickname[j].getFlag());
					
					nickname[j].setString(temp_string);
					nickname[j].setFlag(temp_bool);
				}
			}
		}
		//此处两个循环:短字符串如若包含在长字符串里,则将该nickname的flag设置为true
		for (int i = 0 ; i < n - 1; i++) {
			for (int j = i + 1; j < n; j++) {
				if(nickname[j].getString().contains(nickname[i].getString())){
					nickname[i].setFlag(true);
				}
			}
		}
		return nickname;
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		Scanner scanner = new Scanner(System.in);
		Nickname nickname [] = new Nickname[n];        //对象数组初始化对象
		
		for (int i = 0; i < n; i++) {
			nickname[i] = new Nickname();
			nickname[i].setString(scanner.nextLine()); //读取一行字符串
		}
		
		//调用change()方法,进行排序和判断短字符串是否包含在长字符串里
		change(nickname,n);
		
		for (int x = 0,y = x + 1; x < n - 1 || y < n; x++,y++) {
			String longString, shortString;
			///连续两个if…else语句是为了找到两个flag值为false,且两者中间其他flag没有为false的nickname
			if(nickname[x].getFlag() != false) {
				continue;
			}
			else {
				shortString = nickname[x].getString();
			}
			if (nickname[y].getFlag() != false) {
				continue;
			}
			else {
				longString = nickname[y].getString();
			}
			//先找到两个flag都是false的字符串,长的赋给longString,短的赋给shortString
			int index_i,index_j;
			int count_i = 0, count_j = 0;
			
			//形如:shortString = "ABACC", longString = "BCDABA"
			//短字符串的头部与长字符串尾部有相同部分
			index_i = 0;
			index_j = longString.length() - shortString.length() + 1;
			//从长字符串倒数第k个位置开始比对,其中k是短字符串长度减1
			for (int k = index_j; k < longString.length(); k++) {
				index_j = k;
				while(index_j < longString.length() && shortString.charAt(index_i) == longString.charAt(index_j)) {
					index_i++;
					index_j++;
					count_i++;		
				}
				//while语句执行完毕,如果是因为index_j索引到了longString的末尾,则跳出循环
				if(index_j == longString.length()) {
					break;
				}
				//否则意味着longString还并未索引完就因对应位不相等而跳出循环,则count_i重新计数,继续循环
				else {
					index_i = 0;
					count_i = 0;
					continue;
				}
			}
			//形如:shortString = "CCABA", longString = "ABABCDA"
			//短字符串的尾部与长字符串头部有相同部分
			index_i = 1;
			index_j = 0;
			//从短字符串第2位开始比较
			for (int k = index_i; k < shortString.length(); k++) {
				index_i = k;
				while(index_i < shortString.length() && shortString.charAt(index_i) == longString.charAt(index_j)) {
					index_i++;
					index_j++;
					count_j++;
				}
				//while语句执行完毕,如果是因为index_i索引到了shortString的末尾,则跳出循环
				if(index_i == shortString.length()) {
					break;
				}
				//否则意味着shortString还并未索引完就因对应位不相等而跳出循环,则count_j重新计数,继续循环
				else {
					index_j = 0;
					count_j = 0;
					continue;
				}
			}
			//若果count_i > count_j,则将短字符串剪切到长字符串后面
			if(count_i > count_j) {
				longString = longString + shortString.substring(count_i, shortString.length());
			}
			//若果count_i < count_j,则将长字符串剪切到短字符串后面
			if(count_i <= count_j) {
				longString = shortString.substring(0, shortString.length() - count_j) + longString;
			}
			//新得到的字符串放到长字符串nickname[y]中,同时将短字符串nickname[x]的标记设置为true
			nickname[y].setString(longString);
			nickname[x].setFlag(true);
			//重新排序,以保证新的字符串仍是按长度排好序
			change(nickname, n);
		}
		
		//定义一个空字符串
		String s = "";
		//将剩余标记仍然为false的字符串连在一起
		for (int i = 0; i < n; i++) {
			if(nickname[i].getFlag() == false) {
				s = s + nickname[i].getString();
			}
		}
		
		//此行输出最短昵称字符串,题目不要求输出,上机时可注释掉,这里为了让大家看到结果,更好理解
		System.out.println(s);
		//输出s的字符串长
		System.out.println(s.length());
  
		//输入关闭
		sc.close();
		scanner.close();
	}
}

运行结果

我们再多输入几个例子来测试一下:

        


以上是本次两道Java机试题

如有不足,欢迎批评指正

欢迎阅读上一篇:Java 100道典型机试笔试题(01)

欢迎阅读下一篇:Java 100道典型机试笔试题(03)


作者:小南瓜

日期:2019年4月17日11:06

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
很抱歉,作为一个 AI,我并没有具备出题和编写代码的能力。不过,我可以为您提供一些参考资料,帮助您自己出一套前端笔试题。 以下是一些可能的题范例: 1. 请简述 HTML5 新增的语义化标签,并举例说明它们的作用。 答案:HTML5 新增了一些语义化标签,如 `<header>`、`<footer>`、`<nav>`、`<article>`、`<section>` 等。它们的作用是让页面结构更加清晰,便于搜索引擎、屏幕阅读器等工具理解页面内容,也有助于开发者更好地组织页面代码。 2. 请编写一个 JavaScript 函数,判断一个字符串是否是回文字符串。 答案: ```javascript function isPalindrome(str) { return str === str.split('').reverse().join(''); } ``` 3. 请简述 CSS 中的盒模型,并说明 `box-sizing` 属性的作用。 答案:CSS 中的盒模型指的是一个 HTML 元素所占用的空间,包括了内容区域、内边距、边框和外边距。`box-sizing` 属性可以控制盒模型的计算方式,有两个可选值:`content-box` 和 `border-box`。`content-box` 表示元素的宽度和高度只包括内容区域,而 `border-box` 表示元素的宽度和高度包括了内容区域、内边距和边框。 4. 请编写一个 CSS 样式,使得所有 `<a>` 元素在鼠标悬停时背景色变为灰色。 答案: ```css a:hover { background-color: gray; } ``` 5. 请简述 JavaScript 中的事件冒泡和事件捕获,并说明如何阻止事件冒泡传播。 答案:JavaScript 中的事件冒泡指的是事件从子元素依次向上传播到父元素的过程,而事件捕获则是从父元素依次向下捕获到子元素的过程。可以使用 `event.stopPropagation()` 方法阻止事件冒泡传播。 希望这些例子可以帮助您出一套合适的前端笔试题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值