思路
根据/
字符,将字符串划分成多个子串,再利用一个栈,对划分之后的子串进行求解,具体地:
- 如果子串为
.
,子串不入栈 - 如果子串为
..
- 如果栈非空,栈顶元素出栈
- 否则忽略这个子串
- 否则将子串入栈
最后将栈中的子串拼接起来即可。
思路比较简单,这里主要记录一下split的实现。
split实现
这一题重点在与如何将字符串根据某个字符划分成多个子串,下面记录一下实现时的一些想法。
思路一
遍历字符串,维护两个索引:
- last,
s[last]
是非分隔符,且其之前的字符均已被处理过 - next_c,
s[next_c]
是分隔符,并且是last之后的第一个分隔符
每次对两个索引进行更新,并且把处理过的字符串加入到结果中。
举个例子,对于字符串/home/user/repo
,初始时last==1; s[last]=='h'
。
这种实现,会丢失所有空串,也就是说像这种//
被split之后的结果是空。
在此题中这是有用的。
代码如下:
template<typename UnaryFunction>
void split(char c, const string& s, UnaryFunction add) {
int last = 0;
// 初始化last, next_c
while (last < s.length() && s[last] == c)
last++;
int next_c = last+1;
while (last < s.length()) {
// 后移next_c
while (next_c < s.length() && s[next_c] != c)
next_c++;
// [last, next_c)是一个分割出来的子串
add(s.substr(last, next_c-last));
last = next_c+1;
while (last < s.length() && s[last] == c)
last++;
next_c = last+1;
}
}
思路二
思路一的解法会跳过所有空串,但是在某些情况下我们不希望跳过,而是希望输出,下面是第二种split的实现思路,不会跳过空串。
维护两个索引:
- last,
s[last]==c
,是上一个分隔符。 - next,
s[next]==c
,是last之后的下一个分隔符,[last,next)
之间的子串未被处理过。
同样,在遍历的过程中,更新两个索引,处理两个索引位置之间的子串,加入到结果中。
代码如下:
template<typename UnaryFunction>
void split2(char c, const string& s, UnaryFunction add) {
int last = 0; // 注意这个初始值,不要漏了分隔符开头的情况
int next = 0;
while (last < s.length()) {
// 移动next,找下一个分隔符
while (next < s.length() && s[next] != c)
next++;
// 添加到结果中
if (next == 0) {
// 分隔符开头
add("");
} else
add(s.substr(last+1, next-last-1));
// 更新索引
last = next;
next = last+1;
}
}