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

时间:2014-07-28 15:14:51

标签: regex string rust

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

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);

我无法在恒定的时间内弄清楚如何做到这一点并且不使用额外的空间。我知道我可以使用&#34;替换&#34;但是替换是O(n)。我可以制作一个角色的矢量,但这需要额外的空间。

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

3 个答案:

答案 0 :(得分:6)

一般情况下?对于任何一对角色?这是不可能的。


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

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

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

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

  • 单个Unicode代码点可能由1到4个字节
  • 表示
  • 字形可能由1个或更多字节(无上限)
  • 表示
因此,“替换字符i”的概念带来了一些挑战:

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

一般情况下?这是不可能的。

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

答案 1 :(得分:4)

从Rust 1.27开始,您现在可以使用String::replace_range

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

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

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

答案 2 :(得分:2)

如果您只想处理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的{​​{1}}),但它会做你想要的。 如果输入字符串无效&str,则to_/into_ascii的当前实现会失败。有ascii(可能失败的方法的旧命名),但将来可能会重命名为to_ascii_opt(并删除或重命名失败的方法)。