【数据结构与算法】字符串经典题
翻转移位相关方法
右移
s = "abcd123" k = 3
Return "123abcd"
先分别将“abcd”和“123”翻转,再将整个字符串翻转,即可得到结果。
单词翻转
s = "I am a student"
Return "student a am I"
先分别将每个单词翻转,最后将整个句子进行翻转即得到结果
有效的字母异位词
LeetCode:有效的字母异位词
题目描述:
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例:
输入: s = "anagram", t = "nagaram"
输出: true
思想:
1.使用26位计数器
因为本题仅限于小写英文字母,所以使用26位数组来进行标记即可
2.map.getOrDefault(ch, 0)
若不存在则返回0。使用这个函数可稍微优化代码,不会太臃肿。
3.遍历String的优化写法
//方法一
for(int i=0;i<s.length();++i){
char c = s.charAt(i);
}
//方法二
for(char ch : s.toCharArray()){
}
方法二比方法一性能好不少
代码:
我自己的蠢方法,使用hashmap但是特别不优雅
class Solution {
public boolean isAnagram(String s, String t) {
HashMap<Character,Integer> map = new HashMap<>();
Integer v;
for(int i=0;i<s.length();++i){
char c = s.charAt(i);
if((v = map.get(c))!=null){
map.put(c,v+1);
}else{
map.put(c,1);
}
}
for(int i=0;i<t.length();++i){
char c = t.charAt(i);
if((v = map.get(c))!=null){
if(v==1)
map.remove(c);
else
map.put(c,v-1);
}else{
return false;
}
}
return map.isEmpty();
}
}
更加优化的hashmap写法:
public boolean isAnagram_2(String s, String t) {
Map<Character, Integer> map = new HashMap<>();
for (char ch : s.toCharArray()) {
map.put(ch, map.getOrDefault(ch, 0) + 1);
}
for (char ch : t.toCharArray()) {
Integer count = map.get(ch);
if (count == null) {
return false;
} else if (count > 1) {
map.put(ch, count - 1);
} else {
map.remove(ch);
}
}
return map.isEmpty();
}
本题的标准做法:
class Solution {
public boolean isAnagram(String s, String t) {
int[] arr = new int[26];
for(char c:s.toCharArray()){
arr[c-'a'] +=1;
}
for(char c:t.toCharArray()){
arr[c-'a'] -=1;
}
for(int item : arr){
if(item!=0) return false;
}
return true;
}
}
最长回文串
LeetCode:最长回文串
题目描述:
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例:
输入:
"abccccdd"
输出:
7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
思想:
这题主要切入点容易想到:先用数组存储每个字母的出现次数。然后判断这个数组,仅满足多个偶数+一个奇数,才能组成回文数。
注意:
- 存在大写字母,范围应该是65-122;
- 最后判断是否存在奇数,可以用如下方法判断
if(res < s.length()) res++;
这样很巧妙。我一开始做法是在循环内部判断,那样会徒增复杂性
代码:
class Solution {
public int longestPalindrome(String s) {
int[] arr = new int[58];
for(char c : s.toCharArray()){
arr[c - 'A']++;
}
int res=0;
//boolean b=false;
for(int k : arr){
//if(k%2 == 1) b = true;
res += (k/2)*2;
}
if(res < s.length()) res++;
return res;
}
}
同构字符串
/五星/
LeetCode:同构字符串
题目描述:
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
示例:
输入: s = "paper", t = "title"
输出: true
思想:
这题我根本没想到切入点。看了答案才明白。
遍历到某个元素时,上一次出现该元素的位置,如果不相等就不同构,相等则继续遍历
想到这一点就好做了
代码:
class Solution {
public boolean isIsomorphic(String s, String t) {
int[] arr_s = new int[128];
int[] arr_t = new int[128];
for(int i=0;i<s.length();++i){
char ch1=s.charAt(i),ch2=t.charAt(i);
if(arr_s[ch1]!=arr_t[ch2]){
return false;
}
arr_s[ch1]=arr_t[ch2]=i+1;
}
return true;
}
}
回文子串
LeetCode:回文子串
题目描述:
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。
示例:
输入: "aaa"
输出: 6
说明: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa".
思想:
- 回文串中心位置进行扩展,累计可以扩展的次数,遍历完所有中心位置之后,即得到结果;
- 回文串有两种,奇数串和偶数串。可以通过low和high来控制;
代码:
class Solution {
public int countSubstrings(String s) {
int res = 0;
char[] arr = s.toCharArray();
for(int i=0;i<s.length();++i){
res +=expandCount(arr,i,i);
res +=expandCount(arr,i,i+1);
}
return res;
}
private int expandCount(char[] arr, int low, int high){
int count = 0 ;
while(low>-1&&high<arr.length){
if(arr[low]!=arr[high])
break;
++count;
--low;
++high;
}
return count;
}
}
替换空格
牛客:替换空格
题目描述:
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思想:
如果按照正常思路,遍历字符串,碰到空格就插入“%20”,这样时间复杂度是O(n*n)。
正确思路:先遍历一遍字符串,长度扩展为最终长度,然后使用双指针从后向前遍历,逐个字符赋值,碰到空格特殊处理,直到遍历完整个字符串。时间复杂度为O(n)。
代码:
public class Solution {
public String replaceSpace(StringBuffer str) {
int lenRaw = str.length();
for(int i=0;i<lenRaw;++i){
if(str.charAt(i)==' '){
str.append(" ");
}
}
int pos=lenRaw-1;
for(int i=str.length()-1;i>=0;--i){
char ch = str.charAt(pos--);
if(ch!=' '){
str.setCharAt(i,ch);
continue;
}
str.setCharAt(i--,'0');
str.setCharAt(i--,'2');
str.setCharAt(i,'%');
}
return str.toString();
}
}