题目:
你有一个破损的键盘。键盘上所有的键都可以正常工作,但有时候Home键或者End键会自动按下。你并不知道键盘存在这一问题,而是专心打稿子,甚至连显示器都没打开。当你打开显示器后,展现在你面前的是一段悲剧文本。你的任务是在打开显示器之前计算出这段悲剧文本。
输入包含多组数据。每组数据占一行,包含不超过100000个字母、下划线、字符“[”或者“]”。其中字符“[”表示Home键,“]”表示End键。输入结束标志为文件结束符(EOF)。输入文件不超过5MB。对于每组数据,输出一行,即屏幕上的悲剧文本。
输入:
This_is_a_[Beiju]_text
[[]][][]Happy_Birthday_to_Tsinghua_University
输出:
BeijuThis_is_a_text
Happy_Birthday_to_Tsinghua_University
分析:
对于此题,可引入一个变量来保存字符输入的光标位置,根据我们的日常输入习惯可知,改变光标位置时,每个字符都会进行移动。而题目中有每行输入的数据“包含不超过100000个字符”的条件,显然字符数量较为庞大,不适合使用数组求解,因为数组元素为顺序存储,在数组某个位置插入新元素时,会导致大量的数据移动,效率低,耗时大,不适合用于此题的求解。
链表相对数组而言,在数据的移动方面有着很大优势,因此,本题采用链表的存储方式求解。
(注:并非引入真正的链表,而是采用链表的思想,引入两个数组分别作为链表的数据域和指针域)
package uva;
import java.util.*;
public class Uva11988 {
public static void main(String[] args){
//题目说明每行输入字符数目不超过100000个,故加5防止超出
int maxn = 100000+5;
//last输入字符串的最后一个字符后面的位置
//cur表示光标的当前位置
//next[]在这里相当于链表的指针域
int last, cur, next[];
next = new int [maxn];
//s[]数组相当于链表的数据域
char s[] =new char[maxn];
//s[0]相当于链表的头结点,不存放数据
s[0] = 0;
//字符串str起缓冲作用,Java语言无法直接从键盘输入char型数据
String str;
Scanner sc = new Scanner (System.in);
while(sc.hasNext()){
str = sc.next();
//将输入的字符串赋值给s[]数组,注意从s[1]开始赋值,因为s[0]为头结点,不存放数据
for(int i = 0; i < str.length(); i ++){
s[i+1] = str.charAt(i);
}
//s[]数组非0元素的长度为1+str.length()
int n = str.length()+1;
//第一次输入时,光标的当前位置与最后位置都在0处
last = cur = 0;
//next[0]相当于头结点的指针,在赋值之前指向null,此处的0就相当于null
next[0] = 0;
for(int i = 1; i <= n; i ++){
char ch = s[i];
//当输入'['时,光标定位至起始位置
if(ch == '[') cur = 0;
//当输入']'时,光标定位至末尾位置
else if(ch == ']') cur = last;
//输入正常文本时,光标顺次后移
else{
next[i] = next[cur];
next[cur] = i;
if(cur == last) last = i;
cur = i;
}
}
for(int i = next[0]; i != 0; i = next[i])
System.out.print(s[i]);
}
}
}
对于以下代码段,理解起来可能较为困难,因此绘制图表来展示代码对输入的处理过程:
for(int i = 1; i <= n; i ++){
char ch = s[i];
//当输入'['时,光标定位至起始位置
if(ch == '[') cur = 0;
//当输入']'时,光标定位至末尾位置
else if(ch == ']') cur = last;
//输入正常文本时,光标顺次后移
else{
next[i] = next[cur];
next[cur] = i;
if(cur == last) last = i;
cur = i;
}
}
for(int i = next[0]; i != 0; i = next[i])
System.out.print(s[i]);