UVA - 11988 Broken Keyboard (a.k.a. Beiju Text)

/*
  1. Home键的作用是将光标定位到文首,End键的作用是将光标定位到文尾
  
  2. 在数组中频繁移动(尤其插入和删除)元素比较低效,如果可能尽量使用链表
  
  3. 虽然一般讲链表会讲到指针,但链表实现方式,并非一定需要指针,例如此题
*/

/*

  ------------------心路历程分割线,可以直接跳过------------------
  还是第一次接触这种,不用指针来表示链表的表示方法,一开始想了很久很久,甚至用了许多笨方法
  
  比如自己举出几个例子,对于每个位置,分别列出i 对应的 next[i] cur last 的数据变化情况...
  
  但后来用excel举出一个例子,一个个填充对应数据时,却发现自己越写越乱,每次到最后,总是觉得前面好像哪里弄错了,但是一步一步后推回去找错,又比从头开始麻烦,于是又清空excel,一个个列出对应情况的3个数据,并找出它们的规律和变化关系
  
  这么死板地套了很久,套不出来就先去吃饭,路上走着走着,突然想到了 cur 和 last的实际意义,以及next[i]本身,和next[i]的每次更新,其实都是有规律可循的,于是赶紧吃个饭打开电脑记下来思路,免得待会又不明白了 T^T
  ------------------心路历程分割线,可以直接跳过------------------
  
  注意:!!!
  对我的代码而言,数组下标从1开始,因为输入时就是以s[1]作为起点的,毕竟是 scanf("%s", s + 1); 这样做的目的是---前面的s[0]可以单独保留,作为“头节点”,这样在将[]里的信息运用“头插法”插入时,表示就会比较方便了
  cur的含义:当前的序号,也就是循环到下一个位置时,下一个位置的前驱,在数组s中的下标(看上去有点绕,但不用指针的话,必然得牺牲一点通俗性的,多看几次还是能明白的),例如对 abc[test]def,当循环到 a时,i为1  cur = i = 1, cur既是当前走到的位置,又是走到的下一个位置,也就是b时,b的前驱字母a,在s中的下标
  
  last:
  注意在没遇到[ 或者是 ]之前,last 是和 cur同步移动的,但是遇到 [以后,last就一直停留在 [ 的前一个位置的对应下标。举个例子
  ...A[...]B...  假设A和B之间的,是这个串中遇到的第一个中括号对,在A之前,last和cur一直同步移动,且都是当时的i,遇到[之后,last就一直停留在A的位置的下标了
  可是保留A的下标又有什么用呢?
  当然有用啦!在遇到]以后,我们要更新]的下一个位置的前驱的下标(也就是B的下标,也就是循环到]时,当时的cur),所以遇到]时,所做的处理就是cur变为last了,毕竟B当然是接到A后面的,因为两个中括号只是代表 Home和End键被无意按下,并不出现在真实的文本中,而[]中括号对里面的内容,都被头插到头节点的后面了
  
  其实从这里也就可以理解,为什么遇到[时,将cur赋值0了,因为[后面的文本就是要移动到文本最前端的,当时的cur代表[的下一个元素的前驱(头结点)在s数组中的下表(0),自然就是 cur = 0的处理了
  
  至于既不是[也不是]的普通内容,那就是每次遇到一个新位置,将其前一个位置的后继next[i]接到自己的后继上(next[i] = next[cur]),再将自己作为前一个位置的后继(next[cur] = i),再为其下一个位置预备好位置前驱,(即移动光标,使cur = i,含义是,循环走到下一个位置时,其前驱位置就是当前位置i了<当然,除非被中括号破坏了>),以及last是为了停在[的前一个位置,所以在此之前,除了出现[和]的情况以外,last总是和cur同步移动,总是同一个值
  
  总结:
  一道比较值得回味的题,如果深入想想,多举几个例子,比如多对括号以]结束整个串,多对中括号,以普通字母结束整个串,等等
  
  以及,这个不用指针来表示链表的方式,真的很神奇啊!而且也很值得琢磨研究,越想越觉得有意思,越想越觉得自己好像有些小细节还有些懵懵懂懂,真的很值得重看!!
*/



#include <cstdio>
#include <cstring>
const int maxn = 1e5 + 5;
int last, cur, next[maxn]; //next[i]存放:i后面接着的数,在s中的下标
//cur表示光标位置,当前光标位于 s[cur]的右边
//last表示显示屏的最后一个字符是s[last] 
char s[maxn];

int main()
{
	while (scanf("%s", s + 1) == 1)
	{
		memset(next, 0, sizeof(next));
		int n = strlen(s + 1); //输入保存在s[1], s[2], s[3]......
		last =  cur = 0;
		next[0] = 0;
		
		for (int i = 1; i <= n; i++)
		{
			char ch = s[i];
			switch(ch)
			{
				case '[': cur = 0; break;
				case ']': cur = last; break;
				default:
					next[i] = next[cur]; //将前一个字符的后缀序号,置为当前字符的后缀序号
					next[cur] = i; //前一个字符的后缀,变为当前元素的序号
					if (cur == last) last = i; //更新“最后一个字符”的标号
					cur = i; //移动光标 
					break;
			}
		}
		
		for (int i = next[0]; i != 0; i = next[i])
		printf("%c", s[i]);
		printf("\n");
		
	}
	return 0;
 } 

posted @ 2017-09-23 19:48  mofushaohua  阅读(178)  评论(0编辑  收藏  举报