假设我有一个长度为N的字符串S,我想执行以下M个操作:
我感兴趣的是在所有M操作之后最终的字符串是什么样的。显而易见的方法是进行实际交换,这会导致O(MN)最坏情况的行为。有更快的方法吗?我试图跟踪索引最终的位置,但我找不到减少运行时间的方法(虽然我有一种感觉O(M lg N + N) - 对于操作和最终阅读 - 是可能的。)
答案 0 :(得分:4)
Splay树可以帮到你,它支持数组中的反向操作,总复杂度为O(mlogn)
答案 1 :(得分:3)
struct node {
struct node *child[2];
struct node *parent;
char label;
bool subtree_flipped;
};
然后你可以为左/右孩子设置一个逻辑的getter / setter:
struct node *get_child(struct node *u, bool right) {
return u->child[u->subtree_flipped ^ right];
}
void set_child(struct node *u, bool right, struct node *c) {
u->child[u->subtree_flipped ^ right] = c;
if (c != NULL) { c->parent = u; }
}
旋转必须保留翻转位:
struct node *detach(struct node *u, bool right) {
struct node *c = get_child(u, right);
if (c != NULL) { c->subtree_flipped ^= u->subtree_flipped; }
return c;
}
void attach(struct node *u, bool right, struct node *c) {
set_child(u, right, c);
if (c != NULL) { c->subtree_flipped ^= u->subtree_flipped; }
}
// rotates one of |p|'s child up.
// does not fix up the pointer to |p|.
void rotate(struct node *p, bool right) {
struct node *u = detach(p, right);
struct node *c = detach(u, !right);
attach(p, right, c);
attach(u, !right, p);
}
使用轮播实施splay
。它应该采取一个"警卫"为了展开而被视为NULL
父级的指针,以便您可以将一个节点展开到根节点,将另一个节点展开到其右侧子节点。执行此操作然后您可以显示翻转区域的两个端点,然后切换根的翻转位和对应于不受影响的段的两个子树。
Traversal看起来像这样。
void traverse(struct node *u, bool flipped) {
if (u == NULL) { return; }
flipped ^= u->subtree_flipped;
traverse(u->child[flipped], flipped);
visit(u);
traverse(u->child[!flipped], flipped);
}
答案 2 :(得分:2)
@F。 Ju是对的,splay树是实现目标的最佳数据结构之一。
但是,如果您不想实施它们,或者 O((N + M)* sqrt(M))的解决方案足够好,您可以执行以下操作:
我们将执行sqrt(M)个连续查询,然后在O(N)时间从头开始重建数组。
为了做到这一点,对于每个查询,我们将存储查询的段[a,b]反转或不反转的信息(如果两次反转某些元素范围,它们将变为未反转)。
此处的关键是在此处维护不相交细分的信息。请注意,由于我们在重建数组之前执行了大多数sqrt(M)查询,因此我们最多只有sqrt(M)个不相交的段,并且我们可以在sqrt(M)时间内对sqrt(M)段执行查询操作。如果您需要详细解释如何“反转”,请告诉我们。这些不相交的部分。
这个技巧在解决类似问题时非常有用,值得了解。
<强>更新强>
在他们的比赛中,我使用我描述的方法在HackerRank上解决了与你的问题完全对应的问题。
以下是problem
这是C ++中的my solution。
以下是the discussion about the problem以及我的方法的简要说明,请在那里查看我的第3条消息。
答案 3 :(得分:1)
我正试图跟踪索引最终的位置
如果你只是想跟随起始数组的一个条目,那么很容易在O(M)时间内完成。
我打算只写伪代码,但是不需要挥手,所以我最终得到了可能有效的C ++。
// untested C++, but it does compile to code that looks right.
struct swap {
int l, r;
// or make these non-member functions for C
bool covers(int pos) { return l <= pos && pos <= r; }
int apply_if_covering(int pos) {
// startpos - l = r - endpos;
// endpos = l - startpos + r
if(covers(pos))
pos = l - pos + r;
return pos;
}
};
int follow_swaps (int pos, int len, struct swap swaps[], int num_swaps)
{
// pos = starting position of the element we want to track
// return value = where it will be after all the swaps
for (int i = 0 ; i < num_swaps ; i++) {
pos = swaps[i].apply_if_covering(pos);
}
return pos;
}