如何在恒定时间内替换字符串中的单个字符并且不使用额外空间?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在恒定时间内替换字符串中的单个字符并且不使用额外空间?相关的知识,希望对你有一定的参考价值。

这不是确切的用例,但它基本上是我想要做的:

let mut username = "John_Smith";
println!("original username: {}",username);
username.set_char_at(4,'.'); // <------------- The part I don't know how to do
println!("new username: {}",username);

我无法弄清楚如何在恒定时间内执行此操作并且不使用额外空间。我知道我可以使用“替换”,但替换是O(n)。我可以制作角色的矢量,但这需要额外的空间。

我认为你可以创建另一个使用像as_mut_slice这样的指针变量,但这被认为是不安全的。有一种安全的方法可以在恒定的时间和空间中替换字符串中的字符吗?

答案

如果您只想处理ASCII,则有单独的类型:

use std::ascii::{AsciiCast, OwnedAsciiCast};

fn main() {
    let mut ascii = "ascii string".to_string().into_ascii();
    *ascii.get_mut(6) = 'S'.to_ascii();
    println!("result = {}", ascii);
}

有一些缺失的部分(如into_ascii&str),但它做你想要的。如果输入字符串无效to_/into_ascii,则ascii的当前实现失败。有to_ascii_opt(可能失败的方法的旧命名),但将来可能会重命名为to_ascii(并删除或重命名失败的方法)。

另一答案

一般来说 ?对于任何一对角色?不可能。


string不是阵列。在某些有限的上下文中,它可以实现为数组。

Rust支持Unicode,这带来了一些挑战:

  • Unicode代码点可能是0到224之间的整数
  • 字形可以由多个Unicode代码点组成

为了表示这一点,Rust字符串(现在)是一个UTF-8字节序列:

  • 单个Unicode代码点可能由1到4个字节表示
  • 字素可能由1个或更多字节表示(无上限)

因此,“替换性格i”的概念带来了一些挑战:

  • 字符i的位置在索引i和字符串的结尾之间,它需要从头开始读取字符串以确切知道在哪里,即O(N)
  • 将第i个字符就地切换到另一个字符要求两个字符占用完全相同的字节数

一般来说 ?不可能。

在特定且非常特殊的情况下,字节索引是已知的并且字节编码是已知的长度方式,它可以通过as_mut_bytes直接修改字节序列返回来实现,unsafe正式标记为String::replace_range,因为您可能无意中损坏了字符串(请记住,这个字节序列必须是UTF-8序列)。

另一答案

从Rust 1.27开始,您现在可以使用let mut username = String::from("John_Smith"); println!("original username: {}", username); // John_Smith username.replace_range(4..5, "."); println!("new username: {}", username); // John.Smith

playground

(Qazxswpoi)

replace_range不会与&mut str合作。如果范围的大小和替换字符串的大小不相同,则必须能够调整底层String的大小,因此需要&mut String。但是在你询问(用另一个单字节字符替换单字节字符)的情况下,它的内存使用和时间复杂度都是O(1)。

VecVec::splice上有类似的方法。它们之间的主要区别在于splice返回一个迭代器,它产生被删除的项目。

以上是关于如何在恒定时间内替换字符串中的单个字符并且不使用额外空间?的主要内容,如果未能解决你的问题,请参考以下文章

在恒定时间内修剪 C++ 字符串

如何从 char 数组中清除单个字符?

如何在多行中替换单个字线?

MS Access查询:使用单个字符替换字符串中的所有字符

电脑系统批处理,如何将多个tab替换为一个tab,而单个的tab不变化?

这意味着啥:在长度恒定的时间内比较两个字符串 $a 和 $b?