Leetcode 题解 - 字符串
文章目录
剑指offer 58 - ll 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
输入: s = "abcdefg", k = 2
输出: "cdefgab"
输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"
class Solution {
public String reverseLeftWords(String s, int n) {
return s.substring(n, s.length()) + s.substring(0, n); //字符串切片与拼接[start,end)
//字符串遍历拼接方法
//String res = "";
//for(int i = n; i < s.length(); i++){
// res += s.charAt(i);
//}
//for(int i = 0; i< n; i++){
// res += s.charAt(i);
//}
//return res;
}
}
796.旋转字符串(简单)
给定两个字符串, A 和 B。A 的旋转操作就是将 A 最左边的字符移动到最右边。 例如, 若 A = ‘abcde’,在移动一次之后结果就是’bcdea’ 。如果在若干次旋转操作之后,A 能变成B,那么返回True.
示例 1:
输入: A = 'abcde', B = 'cdeab'
输出: true
示例 2:
输入: A = 'abcde', B = 'abced'
输出: false
class Solution {
public boolean rotateString(String A, String B) {
//只需比较一下两个字符串的长度,然后判断A + A中是否存在B就ok,因为A + A中已经包含了所有可能的移动情况
if(A.length() != B.length()) return false;
return (A + A).contains(B);
}
}
344.反转字符串(简单)
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
class Solution {
public void reverseString(char[] s) {
int n = s.length;
int start = 0, end = n-1;
while(start < end){
char tmp = s[start];
s[start++] = s[end];
s[end--] = tmp;
}
}
}
541.反转字符串 ll(简单)
给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。
- 如果剩余字符少于 k 个,则将剩余字符全部反转。
- 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
输入: s = "abcdefg", k = 2
输出: "bacdfeg"
class Solution {
public String reverseStr(String s, int k) {
char[] a = s.toCharArray();
for (int start = 0; start < a.length; start += 2 * k) {
int i = start, j = Math.min(start + k - 1, a.length - 1); //j的取值一定要注意!!
while (i < j) {
char tmp = a[i];
a[i++] = a[j];
a[j--] = tmp;
}
}
return new String(a);
}
}
151.翻转字符串中的单词(中等)
给定一个字符串,逐个翻转字符串中的每个单词。
说明:无空格字符构成一个 单词 。
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
输入:"the sky is blue"
输出:"blue is sky the"
输入:" hello world! "
输出:"world! hello"
解释:输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
输入:"a good example"
输出:"example good a"
解释:如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
class Solution {
public String reverseWords(String s) {
StringBuilder sb = trimSpaces(s);
// 翻转字符串
reverse(sb, 0, sb.length() - 1);
// 翻转每个单词
reverseEachWord(sb);
return sb.toString();
}
public StringBuilder trimSpaces(String s) {
int left = 0, right = s.length() - 1;
// 去掉字符串开头的空白字符
while (left <= right && s.charAt(left) == ' ') {
++left;
}
// 去掉字符串末尾的空白字符
while (left <= right && s.charAt(right) == ' ') {
--right;
}
// 将字符串间多余的空白字符去除
StringBuilder sb = new StringBuilder();
while (left <= right) {
char c = s.charAt(left);
if (c != ' ') {
sb.append(c);
} else if (sb.charAt(sb.length() - 1) != ' ') {
sb.append(c);
}
++left;
}
return sb;
}
public void reverse(StringBuilder sb, int left, int right) {
while (left < right) {
char tmp = sb.charAt(left);
sb.setCharAt(left++, sb.charAt(right)); //StringBuffer.setCharAt()用于将字符串中指定的位置的字符替换成目标字符
sb.setCharAt(right--, tmp);
}
}
public void reverseEachWord(StringBuilder sb) {
int n = sb.length();
int start = 0, end = 0;
while (start < n) {
// 循环至单词的末尾
while (end < n && sb.charAt(end) != ' ') {
++end;
}
// 翻转单词
reverse(sb, start, end - 1);
// 更新start,去找下一个单词
start = end + 1;
++end;
}
}
}
557.反转字符串中的单词 lll(简单)
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
输入:"Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"
class Solution {
public String reverseWords(String s) {
String strs[] = s.split(" "); //将字符串以空格为分隔符分割成字符串数组
StringBuilder sb = new StringBuilder();
for(int i = 0; i < strs.length; i++){
sb.append(reverse(strs[i]));
sb.append(" ");
}
return sb.toString().trim();
}
public String reverse(String s){
char[] chs = s.toCharArray(); //将字符串转换为字符数组
for(int i = 0, j = chs.length - 1; i < j; i++, j--){
char temp = chs[i];
chs[i] = chs[j];
chs[j] = temp;
}
return new String(chs); //返回字符串
}
}
242.有效的字母异位词(简单)
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例1:
输入: s = "anagram", t = "nagaram"
输出: true
示例2:
输入: s = "rat", t = "car"
输出: false
方法1(排序):t 是 ss 的异位词等价于「两个字符串排序后相等」。因此我们可以对字符串 ss 和 tt 分别排序,看排序后的字符串是否相等即可判断。此外,如果 ss 和 tt 的长度不同,tt 必然不是 ss 的异位词。
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()) return false;
char[] ch1 = s.toCharArray();
char[] ch2 = t.toCharArray();
Arrays.sort(ch1);
Arrays.sort(ch2);
return Arrays.equals(ch1, ch2);
}
}
方法2(哈希表):tt 是 ss 的异位词等价于「两个字符串中字符出现的种类和次数均相等」,因此我们用哈希表维护对应字符的频次即可。
class Solution {
HashMap<Character, Integer> map = new HashMap<>();//采用哈希表来存储每个字符出现的次数
for(int i = 0; i < s.length(); i++){
char cur = s.charAt(i);
map.put(cur, map.getOrDefault(cur, 0) + 1); //出现对应字符则次数加一
}
for(int j = 0; j < t.length(); j++){
char cur = t.charAt(j);
map.put(cur, map.getOrDefault(cur, 0) - 1); //出现对应字符则次数减一
if(map.get(cur) < 0){
return false; //若某个字符的次数小于0,则说明不相等
}
}
return true;
}
}
409.最长回文串(简单)
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa"
不能当做一个回文字符串。
输入:
"abccccdd"
输出:
7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
对于每个字符 ch,假设它出现了 v 次,我们可以使用该字符 v / 2 * 2 次,在回文串的左侧和右侧分别放置 v / 2 个字符 ch,如果有任何一个字符 ch 的出现次数 v 为奇数(即 v % 2 == 1),那么可以将这个字符作为回文中心,注意只能最多有一个字符作为回文中心。在代码中,我们用 ans 存储回文串的长度,由于在遍历字符时,ans 每次会增加 v / 2 * 2,因此 ans 一直为偶数。但在发现了第一个出现次数为奇数的字符后,我们将 ans 增加 1,这样 ans 变为奇数,在后面发现其它出现奇数次的字符时,我们就不改变 ans 的值了。
class Solution {
public int longestPalindrome(String s) {
int[] count = new int[128]; //因为字符的 ASCII 值的范围为 [0, 128)
for(char c : s.toCharArray()){
count[c] ++; //统计每个字符出现的次数
}
int ans = 0;
for(int v : count){
ans += v / 2 * 2; //可使用的偶数字符
if(v % 2 == 1 && ans % 2 == 0){ //奇数个数,且ans为偶数,则只加一次
ans ++;
}
}
return ans;
}
}
205.同构字符串(简单)
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。
每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。(简单理解,即双射)
输入:s = "egg", t = "add"
输出:true
输入:s = "foo", t = "bar"
输出:false
**解题思路:**维护两张哈希表,第一张哈希表 s2t 以 ss 中字符为键,映射至 tt 的字符为值,第二张哈希表 t2s 以 tt 中字符为键,映射至 ss 的字符为值。从左至右遍历两个字符串的字符,不断更新两张哈希表,如果出现冲突时说明两个字符串无法构成同构,返回false。
class Solution {
public boolean isIsomorphic(String s, String t) {
HashMap<Character, Character> s2t = new HashMap<>(); //s到t的映射表
HashMap<Character, Character> t2s = new HashMap<>(); //t到s的映射表
for(int i = 0; i < s.length(); i++){
char x = s.charAt(i);
char y = t.charAt(i);
if((s2t.containsKey(x) && s2t.get(x) != y) || (t2s.containsKey(y) && t2s.get(y) != x)){
return false; //若当前字符对应的字符与哈希表中存储的对应字符不一致,则返回false
}
s2t.put(x, y);
t2s.put(y, x);//存储映射关系
}
return true;
}
}
相似题目:290.单词规律
647.回文子串(中等)
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
解题思路:中心扩展——枚举每一个可能的回文中心,然后用两个指针分别向左右两边拓展,当两个指针指向的元素相同的时候就拓展,否则停止拓展。这里需要注意的是,要考虑回文串是奇数和偶数两种情况,奇数时回文中心是一个字符,偶数时回文中心是两个字符,即需要考虑以i为中心和(i, i + 1)为中心得情况。
class Solution {
private int sum = 0;
public int countSubstrings(String s) {
for(int i = 0; i < s.length(); i++){
extendString(s, i, i); //奇数为回文中心的个数
extendString(s, i, i+1); //偶数为回文中心的个数
}
return sum;
}
public void extendString(String s, int left, int right){
//从中心向两边扩展,只要左右字符相等则回文串个数就加1
while(left >=0 && right < s.length() && s.charAt(left) == s.charAt(right)){
left --;
right ++;
sum ++;
}
}
}
9. 回文数(简单)
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
输入:x = 121
输出:true
输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
解题思路:(1)将数字转换成字符串,但是,这需要额外的非常量空间来创建问题描述中所不允许的字符串。
(2)将数字本身反转,然后将反转后的数字与原数字比较,可能会遇到整数溢出问题;
(3)将整数分成左右两部分,右边那部分需要转置,然后判断这两部分是否相等。
class Solution {
public boolean isPalindrome(int x) {
if(x == 0) return true; //x为0时为回文数
if(x < 0 || x % 10 == 0){ //x为负数或者个位为0时都不可能是回文数
return false;
}
int right = 0;
while(x > right){ //临界条件:即反转后的右边部分数字大于左边部分数字
right = right * 10 + x % 10;
x = x / 10;
}
return x == right || x == right / 10; //记住有两种情况,奇数个数和偶数个数
}
}
696. 计数二进制子串(简单)
给定一个字符串 s,计算具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是连续的。
重复出现的子串要计算它们出现的次数。
输入: "10101"
输出: 4
解释: 有4个子串:“10”,“01”,“10”,“01”,它们具有相同数量的连续1和0。
class Solution {
public int countBinarySubstrings(String s) {
int preLen = 0, curLen = 1, ans = 0;
for(int i = 1; i < s.length(); i++){
if(s.charAt(i) == s.charAt(i-1)){
curLen ++; //当前字符与之前字符相等,则当前字符长度+1
}else{
preLen = curLen;//不相等时就将当前长度赋给pre,同时当前长度置1
curLen = 1;
}
if(curLen <= preLen) ans++;//关键语句:当cur长度小于pre时则结果加1(连续的0长度与连续的1长度之间取最小值)
}
return ans;
}
}