String字符串的一些基本知识
首先我们要知道的是Java中的String是不可变的 我们做的增删改等操作都是会再新建一个对象进行操作 因此Java中无法做到在原先的字符串中原地操作 为了避免创建很多对象我们可以采取转换成char数组或者使用StringBuilder
一些常用API
详见W3School 主要需要掌握charAt、indexOf、length、split等
反转字符串
思路
可以类比数组的反转 很容易想到用双指针去完成
代码
class Solution {
public void reverseString(char[] s) {
int l = 0;
int r = s.length - 1;
while (l < r) {
s[l] ^= s[r]; //构造 a ^ b 的结果,并放在 a 中
s[r] ^= s[l]; //将 a ^ b 这一结果再 ^ b ,存入b中,此时 b = a, a = a ^ b
s[l] ^= s[r]; //a ^ b 的结果再 ^ a ,存入 a 中,此时 b = a, a = b 完成交换
l++;
r--;
}
}
}
// 第二种方法用temp来交换数值更多人容易理解些
class Solution {
public void reverseString(char[] s) {
int l = 0;
int r = s.length - 1;
while(l < r){
char temp = s[l];
s[l] = s[r];
s[r] = temp;
l++;
r--;
}
}
}
这里讲解一下异或是如何完成交换操作的:
s[i] ^= s[j];
s[j] ^= s[i];
s[i] ^= s[j];
- 这段代码是用来交换数组s中第i个元素和第j个元素的值的。它使用了异或运算符(^),它的作用是对两个二进制数的每一位进行逻辑异或,即相同为0,不同为1。例如,5 ^ 3 = 6,因为5的二进制是101,3的二进制是011,异或后得到110,即6。
这段代码的原理是利用了异或运算的以下性质:
1、任何数和自己异或都等于0,即x ^ x = 0
2、任何数和0异或都等于自己,即x ^ 0 = x
3、异或运算满足交换律和结合律,即x ^ y = y ^ x,(x ^ y) ^ z = x ^ (y ^ z)
所以,当执行第一句s[i] ^= s[j]时,相当于把s[i]和s[j]的值异或后赋给s[i]。此时s[i]变成了原来s[i]和s[j]的值的异或结果。
当执行第二句s[j] ^= s[i]时,相当于把新的s[i]和原来的s[j]再次异或后赋给s[j]。由于异或运算满足交换律和结合律,所以这相当于把原来的s[i]和原来的s[j]两次异或后赋给了s[j]。根据性质1和2,我们知道这就相当于把原来的s[i]赋给了新的s[j]。此时实现了一半交换。
当执行第三句s[i] ^= s[j];时,相当于把新的sj和新的si再次异或后赋给新的 s[i].由于同样满足交换律和结合律,所以这相当于把原来两者三次异或后赋给了新 的 s [i].根据性质1 和2 ,我们知道这就相当于把原来 的 s [j ] 赋 给 了 新 的 s [i ].此时实现了完全交换。
反转字符串II
思路
题目要求给定一个字符串 s 和一个整数 k,从字符串开头算起, 每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
这道题我们需要记住**当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。**
比如我们int i = 0然后让i一次移动2k个单位 这样我们的i每次移动到要处理段的起点 然后再判断剩余字符是否满足大于k个
代码
class Solution {
public String reverseStr(String s, int k) {
//StringBuilder stringBuilder = new StringBuilder(s);
char[] chars = s.toCharArray(); //String转为数组
for (int i = 0; i < chars.length; i += 2 * k) {
if (i + k > chars.length) { //不足k个 全部反转
reverse(chars, i, chars.length - 1);
} else {
reverse(chars, i, i + k - 1);
}
}
return new String(chars);
}
private void reverse(char[] chars, int start, int end) {
while (start < end) {
//同样用异或的方法
chars[start] ^= chars[end];
chars[end] ^= chars[start];
chars[start] ^= chars[end];
start++;
end--;
}
}
}
替换数字
思路
这道题Java不能像C++一样直接操作字符串 但是其中的思想是值得学习的
题目要求遇见数字就替换成number 依然是采用双指针的想法 同时我们要注意:从后向前填充 避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
代码
这里提供java实现的代码 比较容易 使用StringBuilder然后append即可
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s = in.nextLine();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (Character.isDigit(s.charAt(i))) { //内置函数判断是否是数字
sb.append("number");
}else sb.append(s.charAt(i));
}
System.out.println(sb);
}
}
翻转字符串里的单词
思路
这道题可谓是很难了 综合了多种操作 去除空格的同时还要完成反转的操作
首先我们可以先翻转整个字符串 然后再去反转单个单词 这样就可以完成我们想要的效果
首先讲一下如何去除空格 这其实很类似于数组的移除元素操作 只不过这里的元素替换成了空格
既然类似于数组的移除元素操作 我们还是要用双指针去完成操作:
fast和slow指向开头 然后进行遍历 还是fast指向要添加的元素 slow指向要添加的下标 但是这里我们还需要注意单词之间还要加空格 那怎么实现呢? fast 遇到空格或遍历到字符串末尾,就证明遍历完一个单词了 那在这个单词之后我们就要加上一个空格
代码
class Solution {
//用 char[] 来实现 String 的 removeExtraSpaces,reverse 操作
public String reverseWords(String s) {
char[] chars = s.toCharArray();
//1.去除首尾以及中间多余空格
chars = removeExtraSpaces(chars);
//2.整个字符串反转
reverse(chars, 0, chars.length - 1);
//3.单词反转
reverseEachWord(chars);
return new String(chars);
}
//1.用 快慢指针 去除首尾以及中间多余空格,可参考数组元素移除的题解
public char[] removeExtraSpaces(char[] chars) {
int slow = 0;
for (int fast = 0; fast < chars.length; fast++) {
//先用 fast 移除所有空格
if (chars[fast] != ' ') {
//在用 slow 加空格。 除第一个单词外,单词末尾要加空格
if (slow != 0)
chars[slow++] = ' ';
//fast 遇到空格或遍历到字符串末尾,就证明遍历完一个单词了
while (fast < chars.length && chars[fast] != ' ')
chars[slow++] = chars[fast++];
}
}
//相当于 c++ 里的 resize()
char[] newChars = new char[slow];
System.arraycopy(chars, 0, newChars, 0, slow);
return newChars;
}
//双指针实现指定范围内字符串反转,可参考字符串反转题解
public void reverse(char[] chars, int left, int right) {
if (right >= chars.length) {
System.out.println("set a wrong right");
return;
}
while (left < right) {
chars[left] ^= chars[right];
chars[right] ^= chars[left];
chars[left] ^= chars[right];
left++;
right--;
}
}
//3.单词反转
private void reverseEachWord(char[] chars){
int fast;
int slow=0;
for( fast =0;fast<=chars.length;fast++){
if(fast == chars.length || chars[fast] == ' '){
reverse(chars,slow,fast-1);
slow=fast+1;
}
}
}
}
右旋转字符串
思路
这道题依然可以用先全部反转再局部反转的策略:
本题中,我们需要将字符串右移n位,字符串相当于分成了两个部分,如果n为2,符串相当于分成了两个部分,如图: (length为字符串长度)
右移n位, 就是将第二段放在前面,第一段放在后面,先不考虑里面字符的顺序,是不是整体倒叙不就行了。如图:
此时第一段和第二段的顺序是我们想要的,但里面的字符位置被我们倒叙,那么此时我们在把 第一段和第二段里面的字符再倒叙一把,这样字符顺序不就正确了。 如果:
其实,思路就是 通过 整体倒叙,把两段子串顺序颠倒,两个段子串里的的字符在倒叙一把,负负得正,这样就不影响子串里面字符的顺序了。
代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = Integer.parseInt(in.nextLine());
String s = in.nextLine();
int len = s.length(); //获取字符串长度
char[] chars = s.toCharArray();
reverseString(chars, 0, len - 1); //反转整个字符串
reverseString(chars, 0, n - 1); //反转前一段字符串,此时的字符串首尾尾是0,n - 1
reverseString(chars, n, len - 1); //反转后一段字符串,此时的字符串首尾尾是n,len - 1
System.out.println(chars);
}
public static void reverseString(char[] ch, int start, int end) {
//异或法反转字符串,参照题目 344.反转字符串的解释
while (start < end) {
ch[start] ^= ch[end];
ch[end] ^= ch[start];
ch[start] ^= ch[end];
start++;
end--;
}
}
}
还有更容易的解法:
import java.util.Scanner;
public class Main {
public static void main(String[] args) { // 添加了String[] args
Scanner in = new Scanner(System.in);
int index = Integer.parseInt(in.nextLine());
String s = in.nextLine();
// 确保index在合理范围内
if (index >= 0 && index <= s.length()) {
System.out.println(s.substring(s.length() - index) + s.substring(0, s.length() - index)); // 将subString改为substring
} else {
System.out.println("Index out of bounds");
}
}
}