显而易见的方法很容易就地完成
void remove_every_kth(char *s, size_t len, int k)
{
// UNTESTED CODE, there might be an off-by-one or a wrong loop boundary
if (k < len)
return;
const char *endp = s + len;
size_t offset = 1;
// we skip the s[i] = s[i] memmove at offset=0.
for (s+=k-1 ; s + offset < endp-(k-1) ; s+=k-1) {
// every iteration copies k-1 characters, and advances s by k-1
memmove(s, s+offset, k-1);
offset++;
}
size_t lastchunk = endp - (s+offset); // some number (less than k) of bytes left in the input
memmove(s, s+offset, lastchunk);
s[lastchunk] = '\0';
}
// equivalently, we could have kept a pointer to the read position,
// like const char* read = s+offset;
// and incremented it by k, while incrementing s by k-1
// for (int i=0 ; i < k ; i++) // implicit length string
// if (!s[i]) return; // check for length < k
Run Code Online (Sandbox Code Playgroud)
由于k是常数,您可以计算在哪里可以找到任何输出位置的输入字符。 out[i] = in[i + i/k]。没有任何数据依赖,所以如果您不需要就地执行此操作,并且您预先知道字符串的长度,那么这当然是多线程的。只需将必要的memcpy调用外包给多个线程即可。(我编写了简单的版本,而memmove不是字符指针循环,以使这一点更加明显,并且对于中型到大型的性能更好k。对于小型 来说,它可能很糟糕k。)
对于就地多线程来说,如果k很大,就会有所收获,因此即使在长字符串的末尾,大部分复制的源和目标也位于同一块内。各工作单位做到:
offset = chunk_number * chunk_size / k字节,前一个工作单元需要读取它们。offset字节保存到临时数组中。memmove(chunk + offset, chunk + offset*2, chunk_size - offset)(即对前一个工作单元不需要的所有字节进行memmove)。对于小型k,就地多线程是徒劳的,因为块中的大多数字节需要被后面块中的字节覆盖。(非常大的块会有所帮助。)