一、字符串的存储与表示
表示方式 结构特点 示例语言/环境
C 风格(以 \0 结尾) 需遍历找结束符,长度 O(n),易溢出 C/C++ 字面量
Pascal 风格(长度前缀) 第一个字节存储长度,最大 255 Delphi / 老式 Pascal
带长度与容量结构 独立存储 len 和 cap,支持动态扩容 C++ std::string,Rust String
短字符串优化 (SSO) 小字符串存放在对象内部栈上,避免堆分配 libstdc++、LLVM libc++
写时复制 (COW) 共享数据,修改时才复制(已少用) 旧版 GCC std::string
二、字符编码
编码 特点 陷阱
ASCII 7 位,128 个字符 无法表示中文等
GB2312 / GBK 中文双字节,兼容 ASCII 与 UTF‑8 互认乱码
UTF‑8 变长 1~4 字节,兼容 ASCII 字符 ≠ 字节,需正确处理多字节
UTF‑16 2 或 4 字节,Windows 常用 代理对(surrogate pair)
UTF‑32 定长 4 字节,简单但空间大 内存浪费
BOM (Byte Order Mark) U+FEFF,标识大小端 某些协议/JSON 不建议使用
关键点:
· len() 在不同语言中可能是字节数(Go/C)、码点数(Python 3)、或者 UTF‑16 单元数(Java)。
· 反转、索引、子串操作必须考虑编码边界。
三、基本操作与复杂度
操作 典型实现复杂度 备注
拼接(concat) O(n + m) 不可变字符串会产生新对象,多次拼接用 StringBuilder
子串(substring) O(1) ~ O(k) Java 早期共享字符数组(可能内存泄漏),Go 重新分配
查找(indexOf) 朴素 O(n*m) 应使用 KMP / BM / 内置优化
替换(replace) O(n) 正则替换可能更慢
分割(split) O(n) 注意正则转义
四、匹配与搜索算法
算法 时间复杂度 场景
朴素匹配 O((n-m+1)*m) 小文本或教学
KMP O(n + m) 单模式、反复回退时(如流式匹配)
Boyer‑Moore 最好 O(n/m),最坏 O(n*m) 大文本、长模式,实际极快
Rabin‑Karp O(n + m),最坏 O(n*m) 多模式、滚动哈希
Aho‑Corasick O(n + Σ 所有模式总长) 同时匹配多个模式(敏感词过滤)
正则表达式 一般线性,但可能指数级回溯 匹配复杂模式,避免灾难性回溯
五、常见算法题型
5.1 双指针 / 滑动窗口
· 无重复最长子串
· 最小覆盖子串
· 字符串排列(异位词)
5.2 动态规划
· 最长公共子序列 (LCS)
· 编辑距离 (Levenshtein)
· 正则表达式匹配 (* 和 .)
· 交错字符串
5.3 回文
· 中心扩展法 O(n²)
· Manacher 算法 O(n) 求最长回文子串
5.4 前后缀 / 子序列
· KMP 的 next 数组应用(重复子串周期)
· 最短回文(在头部补字符)
· 判断子序列(可预处理下一字符位置)
5.5 字典树 (Trie)
· 前缀匹配、自动补全
· 单词搜索(二维网格)
· 最长公共前缀
5.6 后缀结构
· 后缀数组 + LCP 数组(用于重复子串、不同子串数)
· 后缀自动机 (SAM) 的 O(n) 构造
六、安全风险
漏洞类型 典型案例 防御措施
缓冲区溢出 C 的 gets()、strcpy() 未检查长度 使用 fgets()、strncpy() 或 C++ std::string
格式化字符串 printf(user_input) → 任意内存读写 固定格式:printf("%s", user_input)
SQL 注入 "SELECT * FROM users WHERE name = '" + name + "'" 参数化查询 / 转义
命令注入 system("ping " + ip) 使用 API 列表或严格过滤
正则拒绝服务 (ReDoS) (a+)+b 匹配 aaaaaaaaaaX 导致指数回溯 使用非回溯引擎、超时、避免嵌套量词
路径遍历 ../../etc/passwd 规范路径、白名单校验
七、不同语言的字符串特性速览
语言 不可变? 底层 性能注意点
Java 是(String) UTF‑16 数组 + coder 标识 拼接用 StringBuilder,substring 旧版可能持有大数组
Python 是 灵活表示(Latin1/UTF‑16/UTF‑32) 多次拼接用 join(),切片 O(k)
Go 否(底层只读视图) UTF‑8 字节数组,string 不可变 转换 []byte 复制,+ 拼接会重新分配
C++ 否(std::string 可变) 通常带 SSO c_str() 返回的指针在修改后失效
Rust 否(String 可变) UTF‑8,&str 为切片 索引需用 chars() 或 bytes(),避免按字节截断
八、优化技巧总结
· 预分配容量:已知最终长度时,用 reserve() 减少动态扩容。
· 避免重复转换:如循环内反复 toLowerCase() 或编码转换。
· 使用原始字符串:减少转义(如正则、路径)。
· 利用内存布局:连续数组上的 SIMD 指令可加速查找、比较(如 glibc 的 memcmp 用 SSE/AVX)。
· 字符串池(intern):相同内容的字符串复用对象(Java intern(),Python 自动对小字符串驻留)。