V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
GDC
V2EX  ›  程序员

有人来讨论技术吗?如何高效的将字符串中的大小写互换?

  •  1
     
  •   GDC · Jan 29, 2020 · 6280 views
    This topic created in 2291 days ago, the information mentioned may be changed or developed.
    例如,输入 abcXYZhello123 输出 ABCxyzHELLO123 ;

    就是把字符串中的大写全换成小写、小写全换成大写。

    除了逐个字符判断+替换,还有什么更快速高效的方法吗?

    不限语言,最好是 php 或 js 的,仅仅提供思路讨论也行。
    37 replies    2020-01-31 13:19:47 +08:00
    Chingim
        1
    Chingim  
       Jan 29, 2020
    每个字符肯定都要处理一遍, 所以 O(n) 的时间复杂度少不了
    fengtons
        2
    fengtons  
       Jan 29, 2020 via Android
    通过 ascii 码判断并加减完成转换?
    rigortek
        3
    rigortek  
       Jan 29, 2020 via iPhone
    正则表达式
    0TSH60F7J2rVkg8t
        4
    0TSH60F7J2rVkg8t  
       Jan 29, 2020 via iPhone
    ascii 码,一遍循环,<x 的话+x,>x 的话-x,很容易。查下 ascii table 就知道 x 是几了
    GDC
        5
    GDC  
    OP
       Jan 29, 2020
    @rigortek 也想过正则,没想明白怎么整,能详细点吗
    Mithril
        6
    Mithril  
       Jan 29, 2020
    如果是兼容 ASCII 编码的字符串直接位运算就可以。
    KentY
        7
    KentY  
       Jan 29, 2020
    我会用 ascii code 的方法. 有兴趣可以看看 vim 实现的"~"
    GDC
        8
    GDC  
    OP
       Jan 29, 2020
    @Mithril 都是 ASCII 编码,就是 base64 后的字符
    ysc3839
        9
    ysc3839  
       Jan 29, 2020
    应该没有了,用普通的方法,编译器会优化好的。
    https://www.programmingsimplified.com/c/program/c-program-change-case

    不过刚刚在 https://godbolt.org/ 试了一下,用 ctype.h 的 isupper islower toupper tolower,编译出来并没有内联,而是 call 这几个函数。觉得 call 影响性能的话还是直接写判断和加减比较好。
    tonytonychopper
        10
    tonytonychopper  
       Jan 29, 2020
    用啥方法都是 O(n),而且像楼上说到,编译器会优化。
    52coder
        11
    52coder  
       Jan 29, 2020
    @ysc3839 赞同,使用 c 库里的 isupper 等函数没内联暂开的话,可以手写函数 inline,毕竟就判断个 ascii 码范围,应该可以实现内联。
    JerryCha
        12
    JerryCha  
       Jan 29, 2020
    那么有没有什么办法能保证每个字符都遍历到且时间复杂度小于 O(n)呢(比如说 O(logn))
    Mithril
        13
    Mithril  
       Jan 29, 2020   ❤️ 27
    大写字符是 65~90,小写是 97~112
    二进制是 0100 0001~0101 1010 和 0110 0001~0111 1010
    比如
    01010001A,变成
    01100001a

    你可以直接翻转第六位,就是异或个 0010 0000

    这个 0010 0000 在 ASCII 里面代表空格,所以你直接异或一个空格就可以。当然你得首先判断它是字符。

    ASCII 当初就是这么设计的,大小写基本都是对称的位置。
    另外虽然 ASCII 用来编码字符,但是对应数字那部分都是 0011 开头的。你把这部分 mask 掉剩下的就是字符所表示的实际数值。

    兼容的 UTF8 也是一样的。不过正常来说,你要做一个完美的大小写转换,需要先判断 culture 才行。不过简单的可以就这么直接做了。
    thedrwu
        14
    thedrwu  
       Jan 29, 2020 via Android
    希腊字母?
    带 accent/umlaut 的字母?
    不知道 tr 能不能做。至少 vim 里的~可以完美转换。
    wangyzj
        15
    wangyzj  
       Jan 29, 2020
    ascii 吧
    Believer
        16
    Believer  
       Jan 29, 2020
    ascii table
    Bromine0x23
        17
    Bromine0x23  
       Jan 29, 2020
    @JerryCha 每个字符都遍历到 ≡ 时间复杂度至少是 O(n)
    zhx1991
        18
    zhx1991  
       Jan 29, 2020
    ?

    需要遍历就是 O(n) 的
    msg7086
        19
    msg7086  
       Jan 29, 2020 via Android
    你都说了遍历了,比 O n 小的是跳着历。
    @JerryCha
    imn1
        20
    imn1  
       Jan 29, 2020
    位运算
    if (ch & 32)
    ch = ch & 223
    else
    ch = ch | 225
    demos
        21
    demos  
       Jan 29, 2020
    可以按 4 字节来遍历,就可以 O(n-4)了

    <img alt="" src="https://i.loli.net/2020/01/29/4lLg63t1eAhy7qT.png">
    crab
        22
    crab  
       Jan 30, 2020
    判断范围然后异或 0x20
    ericgui
        23
    ericgui  
       Jan 30, 2020 via Android
    hashmap ?提前搞好对应关系?
    augustheart
        24
    augustheart  
       Jan 30, 2020
    最快的方法就只有遍历字符判断这一种。如果要进一步优化,用查表应该比 if 快一丁点,但是不会有多显著
    icyalala
        25
    icyalala  
       Jan 30, 2020   ❤️ 2
    即使都是 O(n), 效率也会相差甚远。
    尤其是带分支的来判断范围的,在输入是混合符号的情况下,分支预测失败会导致效率会降低好几倍。

    查表+unrolling 是我能想到最快的。
    Windsooon
        26
    Windsooon  
       Jan 30, 2020
    1. 如果可以使用额外空间的话,先建立大小写字母的对应哈希表,然后遍历替换。空间复杂度是常量,时间复杂度是 O(n),n 为字符串长度。
    2. 如果不能使用额外空间的话,可以先实现两个辅助函数,一个将大写字母转成小写字母,一个将小写字母转成大写字母。然后遍历字符串,先判断是大写还是小写,然后调用对应的函数。

    不可能存在低于 O(n) 时间复杂度( n 为字符串长度)的方法。

    1. 假设存在这样的算法,不需要遍历所有字符串即可完成替换。
    2. 设立目标字符串 A,选定其中一个字符 a 为此算法无需遍历的。将 a 的大小写翻转,得到新字符串 A'
    3. 因为算法没有遍历 a,所以算法对 A 与 A'得到的结果应该是一样的,不符合实际情况。
    4. 所以不存在这样的算法。
    inhzus
        27
    inhzus  
       Jan 30, 2020 via Android
    @Windsooon 什么哈希函数能比 comp,add/min 的速度更快…
    Windsooon
        28
    Windsooon  
       Jan 30, 2020
    @inhzus 我表达得不够准确,在这个题目下方法 2 会比方法 1 快,因为只需要一次比较和一次加减。
    2exploring
        29
    2exploring  
       Jan 30, 2020   ❤️ 1
    自定义 ascii table,把其中大写和小写字母位置换一下,其余的和标准 ascii table 一样。转的时候直接用字符当下标访问自定义的 ascii table 就是转换后的值。

    这样不用比较,没有分支,也许会比较快,具体怎么样你要跑跑才知道。

    这个方法稍微改一下还可以用于 utf-8 编码的文本。这次自定义的表不是存 ascii 字符了,而是在大小写字母的位置放 0x20,其他位置放 0,操件由直接赋值改为异或后赋值(在许多机器上 a ^= b 和 a = b 的执行效率是一样的),原理上面有提到了。
    2exploring
        30
    2exploring  
       Jan 30, 2020
    @Windsooon 你确定一次比较就能知道是大小写?
    2exploring
        31
    2exploring  
       Jan 30, 2020
    @inhzus 你看我上面说的法子够快吗?不要把 hash 想复杂了,其实就是一个函数映射关系,这个问题里输入值是有限的,且连续的,且数量不大的,这种情况提前打个表,还要多简单。
    flyoungstudio
        32
    flyoungstudio  
       Jan 30, 2020
    Shell:
    echo abcXYZhello123 | tr [a-z] [A-Z]
    alphatoad
        33
    alphatoad  
       Jan 30, 2020 via iPhone
    遇事不决,fpga
    luozic
        34
    luozic  
       Jan 30, 2020 via iPhone
    全部是英文和数字,遍历一次需要 O(n)。有啥方法不用遍历再转换?
    nvkou
        35
    nvkou  
       Jan 30, 2020 via Android
    抛砖引玉。像这种上下文无关的任务可以显卡并行化处理。不过脱离 php 范畴了。
    msaionyc
        36
    msaionyc  
       Jan 30, 2020
    @JerryCha 遍历操作的时间复杂度是不可能小于 O(n)的
    ts8zs
        37
    ts8zs  
       Jan 31, 2020
    直接加个网页字体...
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   4386 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 104ms · UTC 05:30 · PVG 13:30 · LAX 22:30 · JFK 01:30
    ♥ Do have faith in what you're doing.