注意本题最好用双指针方法来解决,因为涉及数组的两个方向,注意双指针可以同步移动,也可以根据条件来进行移动
class Solution {
public void reverseString(char[] s) {
if (s.length == 1 || s.length == 0) return;
for (int i = 0; i < s.length; ++i) {
if (i <= s.length - i -1) {
char temp = s[s.length -1- i];
s[s.length -1- i] = s[i];
s[i] = temp;
} else break;
}
}
}
我原来的写法根据数组的重点位置来进行判断,在重点位置根据奇偶的情况会导致出现问题。最好还是不要用除以2来确定重点,通过i 和s.length - 1 - i来构造双指针,根据终止条件来限制for循环的此处,这样结果更准确。
class Solution {
public void reverseString(char[] s) {
int n = s.length;
for (int left = 0, right = n - 1; left < right; ++left, --right) {
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
}
}
}
利用到上一题的基本方法,但是双指针遍历过程中,尾部指针的位置发生了变化。注意这种大区间里面选择小区间的行为。
class Solution {
public String reverseStr(String s, int k) {
//还是要利用双指针的方法,这里要注意for循环的递增是以2k为区间
char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i += 2*k) {
int start = i;
//尾部的选择来看数组的结尾序号和开始指针算出来的哪个小
int end = Math.min(ch.length - 1, start + k -1);
while (start < end) {
char temp = ch[start];
ch[start] = ch[end];
ch[end] = temp;
start++;
end--;
}
}
return new String(ch);
}
}
利用异或运算来实现来个元素的交换,注意异或运算符号交换律以及结合律。过程可以参考链接
Java中异或运算实现两个整数的交换以及其功能函数实现_小小沸沸要加油的博客-CSDN博客
//解法二(似乎更容易理解点)
//题目的意思其实概括为 每隔2k个反转前k个,尾数不够k个时候全部反转
class Solution {
public String reverseStr(String s, int k) {
char[] ch = s.toCharArray();
for(int i = 0; i < ch.length; i += 2 * k){
int start = i;
//这里是判断尾数够不够k个来取决end指针的位置
int end = Math.min(ch.length - 1, start + k - 1);
//用异或运算反转
while(start < end){
ch[start] ^= ch[end];
ch[end] ^= ch[start];
ch[start] ^= ch[end];
start++;
end--;
}
}
return new String(ch);
}
}
这里对字符串里面的字符的替换方法有很多
(1)利用String自带的api,replace(“”,“”)或者replaceAll(“”,“”)
(2)利用字符串和数组的api,split()(返回字符串数组)和join(“”)的链式编程来进行。
(3)利用stringbuilder或者stringbffer来进行
class Solution {
public String replaceSpace(String s) {
char[] ch = s.toCharArray();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < ch.length; ++i) {
if (ch[i] == ' ') sb.append("%20");
else sb.append(ch[i]);
}
return sb.toString();
}
}
注意这里对字符串的处理办法,首先可以利用常见的api,要注意这些常见api的用法和返回值
class Solution {
public String reverseWords(String s) {
s = s.trim();//去掉头尾的字符串,注意不对原来的字符串进行改变
//利用正则表达式来取得字符串里面不含空格的部分
List<String> wordlist = Arrays.asList(s.split("\\s+"));
Collections.reverse(wordlist);
return String.join(" ",wordlist);
}
}
参考答案解析中的一个思路,通过对字符串进行遍历,利用两个指针来进行单词字符串的选取。
这里要注意当最后一个位空格是,会使得word位“”,注意此时不要和res进行字符串的拼接。
class Solution {
public String reverseWords(String s) {
//参考题目解析中的一个思路,利用for循环挨个取出其中的单词
String res = "";
for (int i =0; i < s.length();) {
String word = "";
//去掉开头的空格位置
while (i < s.length() && s.charAt(i) == ' ') i++;
int j = i;
//让j读取到单词的结尾后的空格处
while (j < s.length() && s.charAt(j) != ' ') j++;
word = s.substring(i,j);
//拼接单词,注意将新的单词放前面,原来的结果放在后面
//注意没加一个单词就在前面加上一个空格,最后取子串的时候从第2个字符开始取就可以
if (word != "") res = " " + word + res;
//去除单词后面的空格
// while (j < s.length() && s.charAt(j) == ' ') j++;
i = j;
}
return res.substring(1);
}
}
这里方法有三种,第一种是利用切片来进行处理,这是需要额外空间最少的,两个就可以
另外一个是利用stringbuilder来进行,对字符串进行遍历。
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder res = new StringBuilder();
for(int i = n; i < s.length(); i++)
res.append(s.charAt(i));
for(int i = 0; i < n; i++)
res.append(s.charAt(i));
return res.toString();
}
}
作者:jyd
链接:https://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/solution/mian-shi-ti-58-ii-zuo-xuan-zhuan-zi-fu-chuan-qie-p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
注意这里可以通过一个取余操作来简化代码
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder res = new StringBuilder();
for(int i = n; i < n + s.length(); i++)
res.append(s.charAt(i % s.length()));
return res.toString();
}
}
作者:jyd
链接:https://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/solution/mian-shi-ti-58-ii-zuo-xuan-zhuan-zi-fu-chuan-qie-p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这里要注意对字符串进行遍历,判断位置是查找串和目标串的第一个字符相等
这里注意if条件里面的判断条件i+n《m不要漏掉
class Solution {
public int strStr(String haystack, String needle) {
if (needle == null) return 0;
int n = needle.length();
int m = haystack.length();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < m;){
//找到第一个字符
while (i < m && haystack.charAt(i) != needle.charAt(0)) i++;
if (i < m && i+n <= m && haystack.substring(i,i+n).equals(needle)) return i;
else i++;
}
return -1;
}
}
class Solution {
public int strStr(String ss, String pp) {
int n = ss.length(), m = pp.length();
char[] s = ss.toCharArray(), p = pp.toCharArray();
// 枚举原串的「发起点」
for (int i = 0; i <= n - m; i++) {
// 从原串的「发起点」和匹配串的「首位」开始,尝试匹配
int a = i, b = 0;
while (b < m && s[a] == p[b]) {
a++;
b++;
}
// 如果能够完全匹配,返回原串的「发起点」下标
if (b == m) return i;
}
return -1;
}
}
kmp算法的解法:详细对于kmp方法的介绍可以看代码随想录,核心关键是next数组的求取
class Solution {
public int strStr(String ss, String pp) {
if (pp == null) return 0;
if (ss.length() < pp.length()) return -1;
int[] next = new int[pp.length()];//创建模式字符串容量的数组
getNext(next,pp);
int j = 0;
for (int i = 0; i < ss.length(); ++i) {
while (j > 0 && ss.charAt(i) != pp.charAt(j)) j = next[j-1];
if (ss.charAt(i) == pp.charAt(j)) j++;
if (j == pp.length()) return i - pp.length() + 1;
}
return -1;
}
public void getNext(int[] next, String str) {
int j = 0;
next[0] = j;//代表字符串第一个字符的最大前后缀相等的长度
//通过for循环来遍历整个字符串,确定每一个字符对应的next数组值
for (int i = 1; i < str.length(); ++i) {
while (j > 0 && str.charAt(i) != str.charAt(j)) j = next[j -1];
if (str.charAt(i) == str.charAt(j)) j++;
next[i] = j;
}
}
}
注意getNext方法和利用next数组来确定回退位置,大体上是相同的,但是一个是比较相同的字符串,一个是比较文本串和模式串。
本题可以查看官方解法的第二个,利用string的indexOf()四个方法里面的一个
然后是学习代码随想录的kmp改进方法,利用最大长度的相等前后缀不包含的部分长度,对字符串总长度取余,模为0则表明可以重复,否则不可以。
class Solution {
public boolean repeatedSubstringPattern(String s) {
if (s.equals("")) return false;
int len = s.length();
char[] chars = s.toCharArray();
int[] next = new int[len];
int j = 0;
next[0] = j;
// 构造 next 数组过程,j从0开始(空格),i从2开始
for (int i = 1; i < len; i++) {
// 匹配不成功,j回到前一位置 next 数组所对应的值
while (j > 0 && chars[i] != chars[j]) j = next[j-1];
// 匹配成功,j往后移
if (chars[i] == chars[j]) j++;
// 更新 next 数组的值
next[i] = j;
}
// 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
if (next[len-1] > 0 && len % (len - next[len-1]) == 0) {
return true;
}
return false;
}
}