有人来讨论技术吗?如何高效的将字符串中的大小写互换? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答术问题时复制粘贴 AI 生成的内容
GDC
V2EX    程序员

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

  •  1
     
  •   GDC 2020-01-29 21:13:18 +08:00 5844 次点击
    这是一个创建于 2084 天前的主题,其中的信息可能已经有所发展或是发生改变。
    例如,输入 abcXYZhello123 输出 ABCxyzHELLO123 ;

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

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

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

    不过刚刚在 https://godbolt.org/ 试了一下,用 ctype.h 的 isupper islower toupper tolower,编译出来并没有内联,而是 call 这几个函数。觉得 call 影响性能的话还是直接写判断和加减比较好。
    tonytonychopper
        10
    tonytonychopper  
       2020-01-29 21:42:36 +08:00
    用啥方法都是 O(n),而且像楼上说到,编译器会优化。
    52coder
        11
    52coder  
       2020-01-29 22:22:36 +08:00
    @ysc3839 赞同,使用 c 库里的 isupper 等函数没内联暂开的话,可以手写函数 inline,毕竟就判断个 ascii 码范围,应该可以实现内联。
    JerryCha
        12
    JerryCha  
       2020-01-29 22:24:44 +08:00
    那么有没有什么办法能保证每个字符都遍历到且时间复杂度小于 O(n)呢(比如说 O(logn))
    Mithril
        13
    Mithril  
       2020-01-29 22:25:34 +08:00   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  
       2020-01-29 23:08:04 +08:00 via Android
    希腊字母?
    带 accent/umlaut 的字母?
    不知道 tr 能不能做。至少 vim 里的~可以完美转换。
    wangyzj
        15
    wangyzj  
       2020-01-29 23:10:42 +08:00
    ascii 吧
    Believer
        16
    Believer  
       2020-01-29 23:13:40 +08:00
    ascii table
    Bromine0x23
        17
    Bromine0x23  
       2020-01-29 23:16:30 +08:00
    @JerryCha 每个字符都遍历到 ≡ 时间复杂度至少是 O(n)
    zhx1991
        18
    zhx1991  
       2020-01-29 23:27:53 +08:00
    ?

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

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

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

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

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

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

    这个方法稍微改一下还可以用于 utf-8 编码的文本。这次自定义的表不是存 ascii 字符了,而是在大小写字母的位置放 0x20,其他位置放 0,操件由直接赋值改为异或后赋值(在许多机器上 a ^= b 和 a = b 的执行效率是一样的),原理上面有提到了。
    2exploring
        30
    2exploring  
       2020-01-30 12:44:45 +08:00
    @Windsooon 你确定一次比较就能知道是大小写?
    2exploring
        31
    2exploring  
       2020-01-30 12:56:06 +08:00
    @inhzus 你看我上面说的法子够快吗?不要把 hash 想复杂了,其实就是一个函数映射关系,这个问题里输入值是有限的,且连续的,且数量不大的,这种情况提前打个表,还要多简单。
    flyoungstudio
        32
    flyoungstudio  
       2020-01-30 13:19:00 +08:00
    Shell:
    echo abcXYZhello123 | tr [a-z] [A-Z]
    alphatoad
        33
    alphatoad  
       2020-01-30 13:25:02 +08:00 via iPhone
    遇事不决,fpga
    luozic
        34
    luozic  
       2020-01-30 13:53:39 +08:00 via iPhone
    全部是英文和数字,遍历一次需要 O(n)。有啥方法不用遍历再转换?
    nvkou
        35
    nvkou  
       2020-01-30 14:05:17 +08:00 via Android
    抛砖引玉。像这种上下文无关的任务可以显卡并行化处理。不过脱离 php 范畴了。
    msaionyc
        36
    msaionyc  
       2020-01-30 17:29:33 +08:00
    @JerryCha 遍历操作的时间复杂度是不可能小于 O(n)的
    ts8zs
        37
    ts8zs  
       2020-01-31 13:19:47 +08:00
    直接加个网页字体...
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3155 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 12:39 PVG 20:39 LAX 05:39 JFK 08:39
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86