关于字符串类的题目,要不要使用库函数呢?
如果使用库函数可以直接做出来,建议不要使用库函数。
如果库函数只是题目的一部分,可以考虑使用库函数。
反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s
的形式给出。
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
使用位运算进行交换操作:
public void reverseString(char[] s) {
int size = s.length - 1;
for (int i = 0; i < s.length / 2; i++) {
int j = size - i;
s[i] ^= s[j];
s[j] ^= s[i];
s[i] ^= s[j];
}
}
一般可以将 反转字符数组 抽取成模板(经常会用到这个函数)
class Solution {
public void reverseString(char[] s) {
reverse(s, 0, s.length - 1);
}
// 反转字符数组
void reverse(char[] cs, int l, int r) {
while (l < r) {
cs[l] ^= cs[r];
cs[r] ^= cs[l];
cs[l++] ^= cs[r--];
}
}
}
反转字符串II
给定一个字符串 s
和一个整数 k
,从字符串开头算起,每计数至 2k
个字符,就反转这 2k
字符中的前 k
个字符。
- 如果剩余字符少于
k
个,则将剩余字符全部反转。 - 如果剩余字符小于
2k
但大于或等于k
个,则反转前k
个字符,其余字符保持原样。
输入:s = "abcdefg", k = 2
输出:"bacdfeg"
class Solution {
public String reverseStr(String s, int k) {
char[] cs = s.toCharArray();
int len = cs.length;
for (int i = 0; i < len; i += (k * 2)) {
// 如果剩余字符少于 k 个,则将剩余字符全部反转
if (len - i < k) reverse(cs, i, len - 1);
// 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样
// else if (len - i < k * 2) reverse(cs, i, i + k - 1); // 可省略
// 每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符
else reverse(cs, i, i + k - 1);
}
return new String(cs);
}
void reverse(char[] cs, int i, int j) {
while (i < j) {
cs[i] ^= cs[j];
cs[j] ^= cs[i];
cs[i++] ^= cs[j--];
}
}
}
替换空格
请实现一个函数,把字符串 s
中的每个空格替换成"%20"。
输入:s = "We are happy."
输出:"We%20are%20happy."
public String replaceSpace(String s) {
StringBuilder sb = new StringBuilder();
for (char c : s.toCharArray()) {
if (c != ' ') sb.append(c);
else sb.append("%20");
}
return sb.toString();
}
反转字符串中的单词**
给你一个字符串 s
,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s
中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s
中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
输入:s = "the sky is blue"
输出:"blue is sky the"
不使用内置 API:
- 去除首尾以及中间多余空格
- 反转整个字符串
- 反转各个单词
class Solution {
public String reverseWords(String s) {
// 1. 去除首尾以及中间多余空格
StringBuilder sb = removeSpace(s);
// 2. 反转整个字符串
reverseString(sb, 0, sb.length() - 1);
// 3. 反转各个单词
reverseEachWord(sb);
return sb.toString();
}
// 去除首尾及中间多余空格
StringBuilder removeSpace(String s) {
int i = 0, j = s.length() - 1;
while (s.charAt(i) == ' ') i++; // 去除首空格
while (s.charAt(j) == ' ') j--; // 去除尾空格
// 去除中间多余空格
StringBuilder sb = new StringBuilder();
while (i <= j) {
char c = s.charAt(i);
if (c != ' ' || sb.charAt(sb.length() - 1) != ' ')
sb.append(c);
i++;
}
return sb;
}
// 翻转字符串
void reverseString(StringBuilder sb, int i, int j) {
while (i < j) {
char tmp = sb.charAt(i);
sb.setCharAt(i++, sb.charAt(j));
sb.setCharAt(j--, tmp);
}
}
// 翻转每个单词
void reverseEachWord(StringBuilder sb) {
int i = 0, j = 1, n = sb.length();
while (i < n) {
// 找到单词末尾
while (j < n && sb.charAt(j) != ' ') j++;
// 翻转单词
reverseString(sb, i, j - 1);
// 更新位置,去找下一个单词
i = j + 1;
j = i + 1;
}
}
}
使用 split 函数:
class Solution {
public String reverseWords(String s) {
StringBuilder sb = new StringBuilder();
String[] ss = s.split("\\s+");
for (int i = ss.length - 1; i >= 0; i--) sb.append(ss[i] + " ");
return sb.toString().trim();
}
}
从后往前遍历,每次找到单词后添加:使用了字符串 API
class Solution {
public String reverseWords(String s) {
StringBuilder sb = new StringBuilder();
int i = s.length() - 1, j = i;
while (i >= 0) {
while (i >= 0 && s.charAt(i) != ' ') i--;
sb.append(s.substring(i + 1, j + 1) + " ");
while (i >= 0 && s.charAt(i) == ' ') i--;
j = i;
}
return sb.toString().trim();
}
}
左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
输入: s = "abcdefg", k = 2
输出: "cdefgab"
利用 substring
函数:(左开右闭)
public String reverseLeftWords(String s, int n) {
return s.substring(n) + s.substring(0, 2);
}
转 char[]
反转 3 次:
class Solution {
public String reverseLeftWords(String s, int n) {
char[] cs = s.toCharArray();
int len = s.length();
reverse(cs, 0, n - 1);
reverse(cs, n, len - 1);
reverse(cs, 0, len - 1);
return new String(cs);
}
void reverse(char[] cs, int i, int j) {
while (i < j) {
cs[i] ^= cs[j];
cs[j] ^= cs[i];
cs[i++] ^= cs[j--];
}
}
}
利用 StringBuilder
拼接:
class Solution {
public String reverseLeftWords(String s, int n) {
char[] cs = s.toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = n; i < cs.length; i++) sb.append(cs[i]);
for (int i = 0; i < n; i++) sb.append(cs[i]);
return sb.toString();
}
}
实现 strStr()*
给你两个字符串 haystack
和 needle
,请你在 haystack
字符串中找出 needle
字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1
。
输入:haystack = "hello", needle = "ll"
输出:2
这题最优解是 KMP,以后学习后再刷!
利用 indexOf
函数:
public int strStr(String haystack, String needle) {
return haystack.indexOf(needle);
}
双指针:
public int strStr(String s1, String s2) {
if (s2 == "" || s1.equals(s2)) return 0;
int l1 = s1.length(), l2 = s2.length();
if (l1 < l2) return -1;
char c1 = s2.charAt(0); // s2 的首字符
for (int i = 0; i < l1 - l2 + 1; i++) {
// 在 s1 中找 s2 的首字符
if (s1.charAt(i) == c1) {
// 开始逐位比较 s1 和 s2
int j = 0;
while (s1.charAt(i + j) == s2.charAt(j)) {
j++;
// 连续 l2 个长度的字符相同, 说明 s1 中包含 s2
if (j == l2) return i;
}
}
}
return -1;
}
滑动窗口:
public int strStr(String s1, String s2) {
if (s2 == "" || s1.equals(s2)) return 0;
int l1 = s1.length(), l2 = s2.length();
if (l1 < l2) return -1;
char c1 = s2.charAt(0); // s2 首字符
for (int i = 0; i < l1 - l2 + 1; i++)
if (s1.charAt(i) == c1 && s2.equals(s1.substring(i, i + l2)))
return i;
return -1;
}
重复的子字符串
给定一个非空的字符串 s
,检查是否可以通过由它的一个子串重复多次构成。
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。
暴力 + 剪枝:
public boolean repeatedSubstringPattern(String s) {
int len = s.length();
for (int i = 1; i <= (len >> 1); i++) {
// 字符串的长度不是串的整数倍, 必然不满足条件
if (len % i != 0) continue;
String sub = s.substring(0, i); // 枚举子串
// 字符串不以该子串结尾, 必然不满足条件
if (!sub.equals(s.substring(len - i))) continue;
if (sub.repeat(len / i).equals(s)) return true;
}
return false;
}
去头去尾:
public boolean repeatedSubstringPattern(String s) {
String ss = s + s;
return ss.substring(1, ss.length() - 1).contains(s);
}