# 字符串常规
本文作者:程序员飞云
字符串中的第一个唯一字符 (opens new window)
# 转换成小写字母
给你一个字符串 s
,将该字符串中的大写字母转换成相同的小写字母,返回新的字符串。
示例 1:
输入:s = "Hello" 输出:"hello"
1
2示例 2:
输入:s = "here" 输出:"here"
1
2示例 3:
输入:s = "LOVELY" 输出:"lovely"
1
2
我们需要明确一下ASCII的范围
a-z: 97-122
A-Z: 65-90
0-9: 48-57
题目里面只需要将大写字母转换为小写字母,我们只需要遍历这个字符串判断是否是大写,然后将对应的字符转换为小写就可以。
public String toLowerCase(String s) {
StringBuilder sb = new StringBuilder();
for(int i=0;i<s.length();i++){
char ch =s.charAt(i);
if(ch>=65 && ch <=90){
ch = (char)(ch+32);
}
sb.append(ch);
}
return sb.toString();
}
2
3
4
5
6
7
8
9
10
11
12
但是我在这里还发现了官方使用的另一个方式进行转换字符,采用了或运算ch |= 32;
我们以大写字符A 65为例,对应的二进制是 1000001 转换为小写字符a,值是97,二进制是 1100001。
32的二进制是 100000
1000001 | 100000 = 1100001
结果正好是97,所以使用或运算能够帮助我们快速转换字符,而不需要进行强转。
# 验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s
,如果它是 回文串 ,返回 true
;否则,返回 false
。
示例 1:
输入: s = "A man, a plan, a canal: Panama" 输出:true 解释:"amanaplanacanalpanama" 是回文串。
1
2
3示例 2:
输入:s = "race a car" 输出:false 解释:"raceacar" 不是回文串。
1
2
3示例 3:
输入:s = " " 输出:true 解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。 由于空字符串正着反着读都一样,所以是回文串。
1
2
3
4
最简单的方式就是先将字符串里面的其他符号都先去除,而且在去除的时候还将大写转换为小写,得到一个只包含小写字母的字符串。然后我们可以使用对撞型双指针从两侧来比较是否是同一个字符。
public boolean isPalindrome(String s) {
String res = lowerChar(s);
int left = 0;
int right = res.length()-1;
while(left<=right){
if(res.charAt(left) != res.charAt(right)){
return false;
}
left++;
right--;
}
return true;
}
// 大写转为小写,并且去除非字母的字符
public String lowerChar(String s){
StringBuilder sb = new StringBuilder();
int left = 0;
while(left < s.length()){
// 不是字母
// 不是字母或数字
while (left < s.length() && !Character.isLetterOrDigit(s.charAt(left))) {
left++;
}
// 检查 left 是否超出字符串索引范围
if (left >= s.length()) {
break;
}
char ch = s.charAt(left);
// 判断大小写
if(ch >= 65 && ch <= 90){
ch |= 32;
}
sb.append(ch);
left++;
}
return sb.toString();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
合并
public boolean isPalindrome(String s) {
StringBuilder sb = new StringBuilder();
int len= s.length();
// 只保留小写字母
for(int i=0;i<len;i++){
char ch = s.charAt(i);
if(Character.isLetterOrDigit(ch)){
sb.append(Character.toLowerCase(ch));
}
}
// 判断新的字符串回文
int slen = sb.length();
int left = 0;
int right = slen-1;
while(left < right){
if(sb.charAt(left) != sb.charAt(right)){
return false;
}
left++;
right--;
}
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 字符串中的第一个唯一字符
给定一个字符串 s
,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1
示例 1:
输入: s = "leetcode" 输出: 0
1
2示例 2:
输入: s = "loveleetcode" 输出: 2
1
2
主要思路就是先使用一个map来存储这些字母出现的次数,然后再遍历一次字符串,找到第一个字符对应的次数为1的字符,直接返回。
public int firstUniqChar(String s) {
HashMap<Character,Integer> map = new HashMap<>();
// 统计次数
for(int i=0;i<s.length();i++){
char ch = s.charAt(i);
map.put(ch,map.getOrDefault(ch,0)+1);
}
// 遍历字符串,找到第一个只出现一次的值
for(int i=0;i<s.length();i++){
if(map.get(s.charAt(i))==1){
return i;
}
}
return -1;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 有效的字母异位词
给定两个字符串 *s*
和 *t*
,编写一个函数来判断 *t*
是否是 *s*
的字母异位词。
**注意:**若 *s*
和 *t*
中每个字符出现的次数都相同,则称 *s*
和 *t*
互为字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram" 输出: true
1
2示例 2:
输入: s = "rat", t = "car" 输出: false
1
2
# 数组排序比较
将字符串转换为数组后,然后将数组重新排序,再比较。
public boolean isAnagram(String s, String t) {
char [] s1 = s.toCharArray();
char [] s2 = t.toCharArray();
Arrays.sort(s1);
Arrays.sort(s2);
if(s1.length != s2.length){
return false;
}
return Arrays.equals(s1,s2);
}
2
3
4
5
6
7
8
9
10
11
12
# 使用map
我们可以使用一个hashmap存储字符串s里面的所有字符以及对应字符出现的个数,然后在遍历字符串t,如果t里面的字符出现了,那么map里面对应的元素的值就要减少,直到为0,如果出现了map存储的元素的值是小于0,意味着肯定不满足条件。
public boolean isAnagram(String s, String t) {
if(s.length()!=t.length()){
return false;
}
` // 將字符串s的里面字符和对应的出现的次数添加到map里面
HashMap<Character,Integer> map = new HashMap<>();
for(int i=0;i<s.length();i++){
char ch = s.charAt(i);
map.put(ch,map.getOrDefault(ch,0)+1);
}
// 遍历字符串t里面的所有字符,如果出现了和字符串s里面对应的元素,那么次数就减1
for(int i=0;i<t.length();i++){
char ch = t.charAt(i);
map.put(ch,map.getOrDefault(ch,0)-1);
if(map.get(ch) < 0 ){
return false;
}
}
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"] 输出:"fl"
1
2示例 2:
输入:strs = ["dog","racecar","car"] 输出:"" 解释:输入不存在公共前缀。
1
2
3
# 纵向扫描
最简单的方式就是将第一个字符串作为模板,其他的字符串都和第一个进行比较,如果对应的字符串字符和第一个字符串的对应的元素不同,那么直接将第一个字符串进行截取。
例如"flower","flow","flight"
flower
为模板
flow
和flower
比较,到w的时候停止比较,将flower
截取成flow
flow
和flight
比较 ,第三个元素是o
和i
,不同,所有停止比较,直接截取fl
返回
纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。
public String longestCommonPrefix(String[] strs) {
if(strs.length<1 || strs == null){
return "";
}
// 第一个字符串的长度
int len = strs[0].length();
// 数组字符串的个数
int count = strs.length;
// 遍历第一个字符串,获取对应的字符
for(int i=0;i<len;i++){
char c = strs[0].charAt(i);
// 遍历数组,将数组里面对应的字符比较
for(int j=1;j<count;j++){
// 如果当前的第一个字符串的长度等于后续遍历数组字符串的长度,或者字符串对应的字符不是字符串1里面的字符,则返回
if(i==strs[j].length() || strs[j].charAt(i)!=c){
return strs[0].substring(0,i);
}
}
}
return strs[0];
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 横向匹配
我们可以将每一个字符串从数组里面拿出来两两匹配找到最小前缀,然后将这个前缀继续和后续的字符串比较。
public String longestCommonPrefix(String[] strs) {
if(strs.length<1 || strs == null){
return "";
}
// 第一个字符串
String prefix = strs[0];
int count = strs.length;
// 遍历字符串数组,匹配前缀
for(int i = 1;i<count;i++){
prefix = longestCommonPrefix(prefix,strs[i]);
if(prefix.length()==0){
break;
}
}
return prefix;
}
// 获取两个字符串的最小公共前缀
public String longestCommonPrefix(String str1,String str2){
int minLen = Math.min(str1.length(),str2.length());
int index = 0;
// 获取最小前缀的下标位置
while(index<minLen && str1.charAt(index) == str2.charAt(index)){
index++;
}
return str1.substring(0,index);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28