PTA前三次题目集总结
1.前言:
2.设计与分析
3.踩坑心得
4.改进建议
5.总结
1.前言:
1. 前三次题目集的重心从对类的使用和设计,类的关系,到类的封装。第一次题目集题目较多,主要是为了让作为初学者的我们能掌握Java的基本操作。第二次题目集题量适中,但增加了使用链表,自定义类要实现Compara接口并重写compareTo方法的新要求。第三次题目量少,重点考察类的封装性和对封装类的使用。
2.我觉得前三次的题目集除了最后一题,考验的是对已学知识的掌握和自学的能力。而最后一题难度大且要求多,难度循序渐进,题目要求和测试点也逐级增加。我认为这种题目对设计类的能力要求高,如果类没设计好,会使得Main函数中的代码非常复杂。 尤其是第三次题目集的最后一题,输入信息太多,一定要设计多个类进行封装使用才行。
2.设计与分析
1.输入
第一次题目集输入比较简单,只用了一个字符数组存储,但是第二,三次就先用了一个字符串数组存储,再使用了正则表达式来判断数组中每个字符串是题目,试卷,答卷等,分别存入不同的字符串数组中。用第二次的代码来简要说明。
分类代码
for(i=0;i<count;i++){
Pattern p1 = Pattern.compile("\\s*#N:\\s*");//匹配器,需要匹配的
Matcher m1 = p1.matcher(sum[i]);
while(m1.find()){
s1[++count1]=sum[i];
}
Pattern p2 = Pattern.compile("\\s*#T:\\s*");//匹配器,需要匹配的
Matcher m2 = p2.matcher(sum[i]);
while(m2.find()){
s2[++count2]=sum[i];
}
Pattern p3 = Pattern.compile("\\s*#S:\\s*");//匹配器,需要匹配的
Matcher m3 = p3.matcher(sum[i]);
while(m3.find()){
s3[++count3]=sum[i];
num++;
}
}
在第三次存储时,新加了测试点:错误的格式信息,无效的**引用。我认为应该在判断后存入,但是我的代码后续引用就有问题,最后只能for循环中引用一个字符串后判断是否格式正确。
2.分析代码
第一次题目集的最后一题:
(1)代码复杂度
度量:
由于第一次题目集较简单,由图中可以看出代码复杂度低。
(2)设计思路
我使用Title类的方法来分割题目信息,用Paper类的方法来交换题目序号,使Main函数中的题目数组由从序号的小排到大,再用Script类的方法通过正则表达式来判断答案正确与否。
(3)重点代码分析
重点代码
public boolean compare() {
String s3="#A:"+s2;
Pattern p = Pattern.compile(s3);
Matcher m = p.matcher(s1);
while (m.find()) {
a = true;
}
return a;
}
第一次中比较答案正确与否是通过将标准答案和所有答案传给类,由类中的方法(正则表达式)判断正确与否。返回类型设为布尔类型,既可以用于输出,也可以用于判断。
第二次题目集的最后一题
(1)代码复杂度
度量:
从图中可以看出,类的复杂度并不高,但是我的分支嵌套的层数非常多。当时我认为在Main函数中用for循环可以解决问题。这是因为当时我对类不熟练且未看到设计建议(参考答题判题程序-1,建议增加答题类,类的内容以及类之间的关联自行设计),导致我只写了两个类。
(2)设计思路
我使用Score类的方法来判断试卷满分是否为100分,如果不为100直接输出,Paper类的方法来判断答案正确与否,大部分功能在Main函数中的循环实现。
(3)重点代码分析
重点代码
for(j=1;j<grade[u].length;j++){
char[] a=grade[u][j].toCharArray();
flag=1;
for(int l=0;l<a.length;l++){
if(a[l]=='-'){
tihao[n]=a[l-1];
flag=0;
n++;//题目数量
}
if(flag==0&&a[l]!='-') {
fen[j-1]+=a[l];
}
}
}
第二次中我将试卷字符串转化为字符数组,再通过比较字符是否等于'-'来判断是否符合3-70这种类型,如果符合则将相应字符(题目编号和分值)存入数组中。
第三次题目集中的最后一题
(1)代码复杂度
度量:
第三次难度比较大,由图中可以看出,代码复杂度,分层嵌套层数和方法数都很多。这一次我不仅定义了6个类,定义了分割,比较等方法,Main函数中还因为我不断地修改代码,也变得更加复杂了。
(2)设计思路
我在获取输入时将输入的字符串传进Examine类检验格式是否正确,不正确就直接输出,再用正则表达式将不同类型的信息存入不同的字符串数组。通过for循环开始匹配试卷号和答卷号相同的试卷,通过Script类和stardantAnswer类获取标准答案和答卷答案,通过Compare类对比正确与否。通过上述操作输出题目信息,最后输出一张答卷得分。我设计的类与类之间的耦合性都很低,所有类都在Main函数中得到实现。
(3)重点代码分析
重点代码
Pattern p1 = Pattern.compile("^#N:\\d*\\s*#Q:.*#A:.*");
Pattern p2 = Pattern.compile("^#T:\\s*\\d+\\s*((\\d+\\s*-\\s*\\d+\\s*)+)$");
Pattern p3 = Pattern.compile("#S:(\\s*[0-9]\\d*)(\\s*#A:[0-9]\\d*)*");
Pattern p4 = Pattern.compile("#X:(\\s*[0-9]\\d*(.*)*(-*))*");
Pattern p5 = Pattern.compile("D:(\\s*N-[0-9]\\d*)");
第三次中有检查格式是否规范的要求,我是定义类中的方法(正则表达式)来判断的。这里的正则表达式非常重要,因为有多个测试点是关于错误格式信息。原本测试点case8我一直没过,但是修改了正则表达式就通过了。这道题非常考验编写正则表达式的能力。
重点代码
public int[] test1() {// 将题号存起来
int i = 0;
int[] tihao = new int[100];
Pattern p = Pattern.compile("[0-9]*-");
Matcher m = p.matcher(s1);
while (m.find()) {
String s = m.group();
String[] data = s.split("-");
tihao[i] = Integer.parseInt(data[0]);
i++;
}
return tihao;
}
第三次我是通过类的方法获取每张试卷的题号和分数,并转化为整形存储进数组中。相较于第二次,这次更加高级方便,不容易出错。
3.踩坑心得
- 在第一次题目集的最后一题中,由于我刚接触这类题目,对类的使用不熟练,导致类的设计比较单一,功能少。同时不会使用正则表达式的group函数,因此我在代码中反复使用split()对字符串进行分割获取信息。
分割字符串
public String unspilit() {
String[] s=s1.split("\\s*#[NQA]:\\s*");
return s[2];
}
在第一次题目集中,我有两个测试点答案错误,当时不知道错误原因,现在我认为就是以上原因导致的。
2. 在第二次题目集的最后一题中,当我的代码都通过题目所给样例时,提交后我才发现还有很多测试点没通过。我依据测试点一个个改代码,但是最终还有四个测试点答案错误。
由于我只定义了一个判断试卷分数是否是100分的类和一个判断标准答案和答卷答案是否相同的类。导致之后发现无效的试卷号的测试点时实现不了,为了分数只能面对结果编程,最终导致这次成绩不理想。
3. 在第三次题目集中的最后一题中,吸取了上一次的教训,我重点设计类和类中的方法,在获取输入的数据更加方便了。由于理解不了部分测试点,我只能通过修改输入看输出有哪些问题。在改代码的过程中,发现有很多条件未判断,只能通过if···else···来解决,导致代码非常混乱。我认为我应该再学习一下如何更好地使用类。最后还有四个测试点时,幸好老师多加了一个样例来解释乱序输入,不然我就一直会误以为#A:6-4中的6是试卷所对应题号。
最后还是有一个测试点未过(空白卷 没有答案的试卷 按正常试卷输出结果)。我使用题目所给样例输入时,输出结果正确。
当我使用多个空白卷输入时
样例
#N:2 #Q:2+2= #A:4
#N:1 #Q:1+1= #A:2
#N:3 #Q:3+4= #A:4
#T:1 1-5 2-8
#T:2 3-5 2-8
#X:20201103 Tom-20201104 Jack-20201105 Www
#S:1 20201103
#S:2 20201104
输出结果
alert: full score of test paper1 is not 100 points
answer is null
answer is null
20201103 Tom: 0 0~0
alert: full score of test paper2 is not 100 points
answer is null
answer is null
20201104 Jack: 0 0~0
由此发现:我的代码判断并输出试卷是在一个方法中,只能在引用这张试卷时在会判断。问了做对这道题的同学,她的输出时在类中进行的,先输出两张试卷是否满分为100分。我猜测空白卷是因为这个没过的。
4.在第三次题目集的最后一题中,测试点空字符过了但是我自己输入空字符去测试时,答案是错误的。
输入
#N:1 #Q:1+1= #A:
#N:2 #Q:1+3= #A:
#N:2 #Q:1+3= #A:
#T:1 1-5 2-3
#X:20201103 Tom
#S:1 20201103 #A:1- #A:2-3 3 #A:3-3
end
输出结果
wrong format:
wrong format:
alert: full score of test paper1 is not 100 points
1+1=~~false
1+3=~33~false
20201103 Tom: 0 0~0
标准答案是空字符,答卷答案也是空字符,输出的也是空字符,但是判断结果是错的。本来我是想着空字符这个测试点过了就得过且过的,但是之后一直修改代码其他测试点也没有过,最终把这个错误修改了之后最后两个测试点乱序输入却过了。
修改后的代码
public boolean compare() {
if(s2.isBlank()){
s2=" ";
}
String ss = "#A:" + number + "-" + s2;
Pattern p = Pattern.compile(ss);
Matcher m = p.matcher(s1);//#S:1 20201103 #A:1- #A:2-3 3 #A:3-3
while (m.find()) {
return true;
}
return false;
}
我是加入了一个判断,如果试卷答案用blank()函数判断为空字符,则赋值一个空格,用新字符串进行判断。
4.改进建议
1.感谢老师开了新题集给我们补练,在我积累了三次题目集的经验后,再去重写一遍时,代码仅仅提交一次就答案正确。
以下是我针对踩坑心得中第一点改进后的代码。我通过正则表达式的group()函数和字符串的trim()函数实现了将题目的信息进行分割,并按照题目要求去了首尾空格。其中s1,s2,s3是类中定义并且在构造函数中赋值,使用get()和set()来获取字符串。这样子可以实现分割并返回多个信息。
修改后代码
public timu(String s){//#N: 1 #Q: 5 +5= #A:10//\\s*#[NQA]:\\s*
String regex = "^#N:\\s*(\\d*)\\s*#Q:(.*)#A:(.*)";
Pattern p=Pattern.compile(regex);//(^#N:\\s*)((\\d*))(\\s*#Q:\\s*)((.*))(#A:\\s*)((.*))
Matcher m=p.matcher(s);
while(m.find()) {
s1=m.group(1).trim();
s2=m.group(2).trim();
s3=m.group(3).trim();
}
}
2.针对踩坑心得中第二点做出的改进:我新加了一个答卷类(用于分离答卷号和答案)和比较类(用来比较正确答案和答卷答案)。
新增答卷类代码
class stardantAnswer {// 将试卷信息保存到一个对象
private String s1;
private int num=-1;
public stardantAnswer(String s1) {
this.s1 = s1;
}
public void setNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
public int consplit() {// 答卷号
Pattern p = Pattern.compile("#S:\\s*([0-9]*)");
Matcher m = p.matcher(s1);// #S:1 #A:2-4 #A:1-5
while (m.find()) {
return Integer.parseInt(m.group(1));
}
return -1;
}
public String[] Answer() {//答案
Pattern p = Pattern.compile("#A:(\\d*)");
Matcher m = p.matcher(s1);//#S:1 #A:5 #A:22
String[] data=new String[100];
while(m.find()) {
data[++num]=m.group(1).trim();
}
return data;
}
}
这个类用于分离答卷号和答案,还有num记录答案数量,用于判断是否有答案缺失。
3.我在测试多张编号相同的试卷和多个编号相同的答卷时出了问题,我认为还应该判断试卷号和答卷号相同的数量,再去进行下一步。
4.在编写代码前应该注意输出顺序,不然在写完代码后再去调整就是大规模的改动了。就像我的代码由两张试卷和两种答卷后,判断试卷是否为满分的输出不是一起输出,而且输出了一张试卷信息后再去输出。
5.在编写代码前应该仔细读题,最好先是由关系图表示,有个整体框架再去写代码,不然容易造成逻辑错误。
5.总结
从这三次题目集,我学到了正则表达式的使用(我认为使用其获取想要的数据非常高效),还学到了如何设计类能让Main函数的代码使用更简便。(ps:但是我重写一次的代码仍在Main函数有循环,因为我习惯于在Main函数输出了,不过整体代码比之前更加简洁明了。)最重要的是面对对象的思维,像我之前写的代码,很多条件未考虑,为了通过测试点,只能Main函数中用for循坏和if···else···来判断,可谓是面对结果编程。但重写一遍后,我改进了类的内容,让其有了更多功能。我在未来要进一步学习正则表达式,学习如何让代码的可维护性更强。
我希望最后一题的测试点能再写得详细点(很多测试点我都没理解到底什么意思),还希望PTA时间能再长一点(题目难度不断增加,压力剧增)。
标签:题目,String,测试点,Pattern,代码,PTA,三次,试卷 From: https://www.cnblogs.com/homework-33/p/18144468