损坏的键盘(又名:悲剧文本)(BROKEN KEYBOARD)

问题描述

在显示屏上盲打一段文字,在这过程中由于键盘的问题,Home键与end键会自动按下,显有一段文本,其中‘[’表示Home键按下,‘]’表示End键按下,求出显示屏上的文字排序

示例输入

This_is_a_[Beiju]_text

[[]][][]Happy_Birthday_to_Tsinghua_University

示例输出

BeijuThis_is_a__text

Happy_Birthday_to_Tsinghua_University

问题分析

废话:

很容易有这样的思路,找出‘[’与‘]’之间的文字将其移到文字的最前面即可,但是对于在一段文字前插入字符,每插入一个字符,在下一次插入时,其耗费的时间便越长,需要剩余文本全体想后移一位。故而,单纯的使用数组是不可取的。

借助于链表的思想,设定一个next,使得每个元素与下一个元素有一定的关联,传统意义上的链表多用指针来表示,但是在算法竞赛中,使用指针实在不是上上之选,而且也过于麻烦,因此我们这样假定:

s[ ]:输入的样本为字符数组,s[0]设为0,字符从s[1]开始输入

next[ ]:s[next[i]]=p[i+1](p[ ]:输出的样本为字符数组) 即next[i]表示s[i]的下一个

对《算法竞赛入门经典》中的样例代码分析:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define maxn 1000
int main()
{
    int last, cur, next[maxn]; //last记录末位置,cur指向当前位置,next[]记录输出顺序
    int i;
    char s[maxn];                   //输入字符串
    while (scanf("%s", s + 1) == 1) //s[0]空出
    {
        last = cur = 0;
        next[0] = 0; //next[i]置0,意为不指向任何值

        for (i = 1; i <= strlen(s + 1); i++)
        {
            if (s[i] == '[')
                cur = 0; //当输入]时,cur光标指向首位置
            else if (s[i] == ']')
                cur = last; //当输入]时,cur光标指向末尾
            else            //当读入其他字符时,插值操作
            {
                next[i] = next[cur];
                next[cur] = i;
                if (last == cur)
                    last = i; //当末尾值和当前光标位置相同时,last和cur一样向i跟进
                cur = i;      //尾光标向后移动
            }
        }
        //顺次输出
        for (i = next[0]; i != 0; i = next[i])
            printf("%c", s[i]);
        printf("\n");
    }
    return 0;
}

 

刘汝佳一上来就把链表的核心抛出来了,我足足看了一下午。
0.为了方便起见,常常在链表第一个元素之前放一个虚拟节点s[0]
1.光标cur,最后一个字符编号last,其实只是由于这题home,end的条件限制,如果用数组建立单链表,只需要next[i]和s[i]
2.其中next[i]是s[i]连的下一个字符的编号,比如:
s[0],next[0]=3连下一个字符->
s[next[0]],next[next[0]]
3.在本题中,如果next[i]=0,说明不知道这个节点连哪个下一个节点,如果全部插入完节点,遇到next[i]=0,就意味着这个链表已经结束
所以有如下对链表遍历的方法:

for (i = next[0]; i != 0; i = next[i])
    printf("%c", s[i]);

 

4.下面说说如何插入
s[i]对应一个next[i],那么s[i]下一个连的是s[next[i]],next[next[i]]
如果在s[i]后面插入s[j]next[j]
只需next[j]=next[i],next[i]=j
新节点j先插到s[i]指向的s[next[i]]的前面,再把前面i的后面连的那个节点改为新插入的那个j


5.再来说说本题
a.只需改变s的输出顺序,输出s[next[i]]
b.多了个[],就是说插入的位置会发生变化。
★c.next[]数组来记录输出顺序。★

加下来使用一组输入演示过程,便于理解,需要提醒的是:下图中s是输入字符串,当输出next的下标对应的s中的字符时,下一个要输出的字符的下标的next下标,是当前next的值

(纠正,下图中是next下标,不是下表)

比如一般输入abc[def]xyz

输入a的时候next[0]=0,输入b的时候next[0]=1,next[1]=0 ,输入c的时候next[2]=next[1]=0,next[1]=i=2,以此类推

当i=4时,读到‘[’的时候,cur置0,光标指到首位,

进入下一个循环,将首位的next值提到next[i]的位置:

变成next[0]=5,next[5]=1,此时last停留在last=4;

接着继续else if循环,知道读到‘]’,此时进入if语句cur=last=4,

继续循环,将会进入else if语句将next[cur]赋值给next[i],将i赋值给next[i],如下:

继续循环直到结束,会得到以下结果:

接下来按照next[]的指示

        for(i=next[0];i!=0;i=next[i])
            printf("%c",s[i]);

next[0]=5 即输出s[5];next[next[0]=5]=6 即输出s[6];next[next[6]=7] 即输出s[7];next[next[6]=7]=1,即输出s[1];以此类推

输出:

defabcxyz

6.悟道可能时算法学习必经之路,多看就明白了

posted @ 2020-07-22 13:42  我等着你  阅读(234)  评论(0)    收藏  举报