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; }