录入操作的本质是在光标后插入新元素,且不改变后面数据与新数据的前驱后继关系。
数组 O(n)
链表 O(1)
因为字符串已经给定,链表实现需要把插入新元素的问题转化为修改元素的后继指针的问题。所以链表直接用后继指针数组和原字符串表示。
新字符串的连接:前后元素在给定的情况下,可以利用前驱的指针获得后继的秩,并以此连接新元素与后继指针;而前驱与新元素的关系可以通过修改前驱指针,使其指向新节点的秩即可连接前驱与新元素。
问题:由于连接了新节点与后继,如何避免生成的最终结果含有“[”、“]”?
方案:两个字符具有转移当前光标的作用,具体来说,“[”转移到头结点,“]”转移到尾节点。对此,遇到该字符后只转移光标指针,不需要录入生成的链表,即不修改后继指针数组。
问题:如何在home后光标随输入而录入新数据并后移后面数据?
1.数组只能后移数据,比较慢。
2.考虑过切割字符串的方法,不过太繁杂,没有去实践。况且需要涉及两个字符串相加,应该和数组一样需要后移数据,比较慢。
3.链表的问题是新元素怎样才能和原始的链表首节点连接,又能使录入的新内容之间连接?
显然需要在光标后插入元素,且该元素的后继为插入前光标的后继,相应的,需要修改光标的前驱的后继指针为新元素。对于首节点连接,可以将光标指向头结点,使新元素能在头结点之后、原首节点之前插入,且保证逻辑顺序。对于新内容的连接,可以通过不断后移光标到插入结点,新节点在光标后、原首结点前插入,且保证逻辑顺序。故能满足逻辑顺序的要求。其实这种插入就是链表结点插入的方式,只是题目没有体现光标这一概念,导致想乱连接关系的实现方案。其次对于头插、尾插和在指定节点后插入元素的三种插入方式的区别一定要搞清,否则会误解为“home表示需要头插,并且后续的录入就会一直头插”,这与不断在光标后录入新内容的插入方式不匹配。
以秩0为结束标记,尾节点的指针指向0。
本题采用数组模拟链表即可。扫描一遍字符串,分类讨论指针的移动情况和插入情况。
插入采用在秩为k的元素后插入秩为i的元素,
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char s[N];
int ne[N];
void add(int k,int i){//在k后插入i
ne[i]=ne[k];//用k的后继指针连接后继
ne[k]=i;//用秩连接前驱k
}
int main(){
freopen("test.txt","r",stdin);
while(~scanf("%s",s+1)){
int cur=0,last=0;//首尾节点指针,保存原始头节点的秩
ne[0]=0;
for(int i=1;s[i];i++)//扫描字符串
{
if(s[i]=='[') cur=0;
else if(s[i]==']') cur=last;
else{
add(cur,i);
if(cur==last)last=i;//当光标在末尾,才能让尾节点指针移动到当前扫描位置
cur=i;//移动到当前扫描位置
//注意移动指针不是单纯的自增,而是跟随扫描位置
}
}
for(int i=ne[0];i;i=ne[i])
cout<<s[i];//字符之间没有间隔
cout<<endl;
}
return 0;
}