1. 字符串基础概念与常见操作
字符串是编程中最基础也最重要的数据类型之一。几乎所有编程语言都提供了丰富的字符串处理功能。在开始解决"代码随想录"中的字符串习题前,我们需要先夯实基础。
字符串本质上是一个字符序列,在内存中通常以连续存储的方式存在。不同语言对字符串的实现略有差异:
- C语言中字符串是以'\0'结尾的字符数组
- Java中String是不可变对象
- Python中str也是不可变类型
- JavaScript中字符串是基本类型但具有对象特性
字符串的常见操作包括:
- 创建与初始化:不同语言语法不同,但基本概念相通
- 长度获取:strlen()、length()、len()等方法
- 连接操作:+运算符或concat方法
- 子串提取:substring()、slice()等
- 查找与替换:indexOf()、replace()等
- 分割与合并:split()、join()等方法
- 大小写转换:toUpperCase()、lower()等
- 去除空白:trim()、strip()等方法
提示:理解字符串的不可变性很重要。在Java、Python等语言中,字符串一旦创建就不能修改,任何看似修改的操作实际上都是创建了新字符串。
2. 字符串习题解析与解题思路
2.1 回文字符串判断
回文字符串是指正读反读都相同的字符串,如"madam"、"racecar"等。判断一个字符串是否为回文是常见的面试题。
基本解题思路:
- 双指针法:一个指针从字符串头部开始,一个从尾部开始,向中间移动并比较字符
- 反转比较法:将字符串反转后与原字符串比较
- 递归法:比较首尾字符,然后递归判断中间子串
以双指针法为例的Java实现:
java复制public boolean isPalindrome(String s) {
int left = 0, right = s.length() - 1;
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
2.2 第一个唯一字符
题目要求找出字符串中第一个不重复的字符。例如在"leetcode"中,'l'是第一个唯一字符。
解题思路:
- 使用哈希表统计每个字符出现的次数
- 第一次遍历记录频率,第二次遍历找出第一个频率为1的字符
- 也可以使用数组代替哈希表,特别是当字符集有限时
Java实现示例:
java复制public int firstUniqChar(String s) {
int[] freq = new int[26];
for (int i = 0; i < s.length(); i++) {
freq[s.charAt(i) - 'a']++;
}
for (int i = 0; i < s.length(); i++) {
if (freq[s.charAt(i) - 'a'] == 1) {
return i;
}
}
return -1;
}
2.3 字符串分割与合并
字符串分割是常见操作,如将"hello,world"按逗号分割成["hello", "world"]。
Java中使用split()方法:
java复制String str = "hello,world";
String[] parts = str.split(",");
合并字符串可以使用join()方法:
java复制String joined = String.join("-", parts); // "hello-world"
注意:split()方法参数是正则表达式,特殊字符如"."、"|"等需要转义
3. 字符串高级操作与性能优化
3.1 字符串匹配算法
字符串匹配是计算机科学中的经典问题,常见算法包括:
- 朴素算法(Brute Force):逐个比较,时间复杂度O(mn)
- KMP算法:利用部分匹配表优化,时间复杂度O(m+n)
- Boyer-Moore算法:从右向左比较,利用坏字符和好后缀规则
- Rabin-Karp算法:基于哈希的算法
以KMP算法为例,其核心是构建部分匹配表(Partial Match Table):
java复制public int[] buildPMT(String pattern) {
int[] pmt = new int[pattern.length()];
pmt[0] = 0;
int len = 0;
int i = 1;
while (i < pattern.length()) {
if (pattern.charAt(i) == pattern.charAt(len)) {
len++;
pmt[i] = len;
i++;
} else {
if (len != 0) {
len = pmt[len - 1];
} else {
pmt[i] = 0;
i++;
}
}
}
return pmt;
}
3.2 字符串拼接性能
在Java中,字符串拼接有几种方式:
- 使用+运算符
- 使用StringBuilder
- 使用StringBuffer(线程安全版StringBuilder)
性能比较:
java复制// 不推荐 - 每次+都会创建新String对象
String result = "";
for (int i = 0; i < 10000; i++) {
result += i;
}
// 推荐 - 使用StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
String result = sb.toString();
经验法则:在循环中拼接字符串一定要使用StringBuilder,单次操作可以使用+
3.3 字符串编码与国际化
处理多语言文本时需要注意字符编码:
- ASCII:7位,仅支持英文字符
- ISO-8859-1:8位,支持西欧语言
- UTF-8:可变长度,支持所有Unicode字符
- UTF-16:固定2或4字节
Java中字符串内部使用UTF-16编码,与外部交互时需要注意转换:
java复制// 从字节数组创建字符串
byte[] bytes = ...;
String str = new String(bytes, "UTF-8");
// 将字符串转为字节数组
byte[] bytes = str.getBytes("UTF-8");
4. 实战案例与常见问题
4.1 手机号脱敏处理
实际开发中经常需要对敏感信息如手机号进行脱敏处理:
java复制public String maskPhoneNumber(String phone) {
if (phone == null || phone.length() < 7) {
return phone;
}
return phone.substring(0, 3) + "****" + phone.substring(7);
}
4.2 字符串与数字转换
类型转换是常见操作,但需要注意异常处理:
java复制// 字符串转整数
try {
int num = Integer.parseInt("123");
} catch (NumberFormatException e) {
// 处理异常
}
// 数字转字符串
String str = String.valueOf(123);
String str2 = Integer.toString(123);
4.3 字符串与日期转换
日期格式化也是常见需求:
java复制SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 字符串转日期
Date date = sdf.parse("2023-05-20");
// 日期转字符串
String dateStr = sdf.format(new Date());
4.4 常见问题与解决方案
- 字符串乱码:确保编码一致,特别是在IO操作时
- 内存问题:大字符串处理可能导致内存溢出,考虑流式处理
- 性能问题:避免在循环中创建大量临时字符串
- 安全性问题:SQL注入、XSS攻击等都和字符串处理不当有关
避坑指南:处理用户输入的字符串时,一定要进行验证和转义,防止安全漏洞
