文章目录
344.反转字符串
541.反转字符串II
【剑指Offer05】替换空格 LCR 122. 路径加密
151.反转字符串中的单词
【剑指Offer】LCR182.动态口令
1.【344】反转字符串
1.1 题目描述
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]
示例 2:
输入:s = [“H”,“a”,“n”,“n”,“a”,“h”]
输出:[“h”,“a”,“n”,“n”,“a”,“H”]
提示:
- 1 <= s.length <= 105
- s[i] 都是 ASCII 码表中的可打印字符
1.2 解题思路
建议:不要使用库函数
此题和206.反转链表类似,可以使用双指针法。对于字符串的反转,比链表简单一些。因为字符串也是一种数组,所以在内存中是连续分布的。
swapa()交换函数有两种实现方式:
1.常见的交换数值,使用临时变量temp
int tmp = s[i];
s[i] = s[j];
s[j] = tmp;
2.通过位运算
s[i] ^= s[j];//构造a^b的结果,放在a中
s[j] ^= s[i];//将a^b的结果再^b,存入b中,b=a,a=a^b
s[i] ^= s[j];//a^b的结果再^a,存入a中,b=a,a=b完成交换
1.3 java代码实现
- 时间复杂度:O(n)
- 空间复杂度:O(1)
class Solution {
public void reverseString(char[] s) {
//使用temp来交换
/*int left=0;
int right=s.length-1;
while (left<right){
char temp=s[left];
s[left]=s[right];
s[right]=temp;
left++;
right--;*/
//使用位运算来实现交换操作
int left=0;
int right=s.length-1;
while (left<right){
s[left] ^= s[right];//构造a^b的结果,放在a中
s[right] ^= s[left];//将a^b的结果再^b,存入b中,b=a,a=a^b
s[left] ^= s[right];//a^b的结果再^a,存入a中,b=a,a=b完成交换
left++;
right--;
}
}
}
2.【541】反转字符串II
2.1 题目描述
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
- 如果剩余字符少于 k 个,则将剩余字符全部反转。
- 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例 1:
输入:s = “abcdefg”, k = 2
输出:“bacdfeg”
示例 2:
输入:s = “abcd”, k = 2
输出:“bacd”
提示:
- 1 <= s.length <= 104
- s 仅由小写英文组成
- 1 <= k <= 104
2.2 解题思路
当需要固定规律一段一段的去处理字符串的时候,要想想在for循环的表达上做做文章
在遍历字符串的过程中,只要让i+=2k,i每次移动2k就可以了,然后判断是否需要有反转的区间
2.3 java代码实现
- 时间复杂度:O(n)
- 空间复杂度:O(1)
class Solution {
public String reverseStr(String s, int k) {
//解法一 :
/* char[] ch=s.toCharArray();
//1.每隔2k个字符的前k个字符进行反转
for (int i=0;i<ch.length;i+=2*k){
//2.如果剩余字符小于2k,但大于或等于k个,则反转前k个字符
if (i+k <=ch.length){
reverse(ch,i,i+k-1);
continue;
}
//3.剩余字符小于k个,则将剩余字符全部反转
reverse(ch,i,ch.length-1);
}
return new String(ch);*/
}
//定义反转函数
public void reverse(char[] ch,int i,int j){
for (;i<j;i++,j--){
char temp=ch[i];
ch[i]=ch[j];
ch[j]=temp;
}
}
}
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){
char temp=ch[start];
ch[start]=ch[end];
ch[end]=temp;
start++;
end--;
}
}
return new String(ch);
}
}
3.【剑指Offer05】替换空格
3.1 题目描述
假定一段路径记作字符串 path,其中以 “.” 作为分隔符。现需将路径加密,加密方法为将 path 中的分隔符替换为空格 " ",请返回加密后的字符串。
示例 1:
输入:path = “a.aef.qerf.bb”
输出:“a aef qerf bb”
3.2 解题思路
使用一个新的对象,复制str,复制的过程对其判断,是分隔符.则替换为空格" ",否则直接复制,类似于数组复制
此题的原题是将字符串s中的空格替换成"%20"
原题的解题思路:双指针法
首先扩充数组到每个空格替换成"%20"之后的大小。然后从后向前替换空格。
其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。
这么做的好处:
- 不用申请新数组。
- 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
3.3 java代码实现
- 时间复杂度:O(n)
- 空间复杂度:O(1)
class Solution {
public String pathEncryption(String path) {
//使用一个新的对象,复制str,复制的过程对其判断,是分隔符.则替换为空格" ",否则直接复制,类似于数组复制
if (path==null){
return null;
}
//选用StringBuilder 单线程使用,比较快
StringBuilder sb=new StringBuilder();
//使用sb逐个复制path,碰到分隔符.则替换为空格" ",否则直接复制
for (int i=0;i<path.length();i++){
//path.charAt(i) 为 char 类型,为了比较需要将其转为和 " " 相同的字符串类型
if (path.charAt(i)=='.'){
sb.append(" ");
}else {
sb.append(path.charAt(i));
}
}
return sb.toString();
}
}
4.【151】反转字符串中的单词
4.1 题目描述
给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = “the sky is blue”
输出:“blue is sky the”
示例 2:
输入:s = " hello world "
输出:“world hello”
解释:反转后的字符串中不能存在前导空格和尾随空格。
示例 3:
输入:s = “a good example”
输出:“example good a”
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
提示:
- 1 <= s.length <= 104
- s 包含英文大小写字母、数字和空格 ’ ’
- s 中 至少存在一个 单词
4.2 解题思路
1.移除多余空格:使用双指针法来移除 和27.移除元素的逻辑是一样的。
2.将整个字符串反转 :参考344.反转字符串和
541.反转字符串II
3.将每个单词反转
4.3 java代码实现
- 时间复杂度:O(n)
- 空间复杂度:O(1)
使用StringBuilder实现
class Solution {
public String reverseWords(String s) {
/**
* 不使用Java内置方法实现
* 1.去除收尾以及中间多余空格
* 2.反转整个字符串
* 3.反转各个单词
*/
StringBuilder sb=removeSpace(s);
reverseString(sb,0,sb.length()-1);
reverseEachWord(sb);
return sb.toString();
}
//去除首尾以及中间多余空格
private StringBuilder removeSpace(String s){
int start=0;
int end=s.length()-1;
while (s.charAt(start)==' ')
start++;
while (s.charAt(end)==' ')
end--;
StringBuilder sb=new StringBuilder();
while (start<=end){
char c=s.charAt(start);
if (c!=' ' || sb.charAt(sb.length()-1)!=' '){
sb.append(c);
}
start++;
}
return sb;
}
//反转字符串指定区间[start,end]的字符
public void reverseString(StringBuilder sb,int start,int end){
while (start<end){
char temp=sb.charAt(start);
sb.setCharAt(start,sb.charAt(end));
sb.setCharAt(end,temp);
start++;
end--;
}
}
//反转每个单词
private void reverseEachWord(StringBuilder sb){
int start=0;
int end=1;
int n=sb.length();
while (start < n) {
while (end<n && sb.charAt(end)!=' '){
end++;
}
reverseString(sb,start,end-1);
start=end+1;
end=start+1;
}
}
}
用 char[] 来实现 String 的 removeExtraSpaces,reverse 操作
class Solution {
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);
}
//去除多余的空格
public static 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]=' ';
slow++;
}
//fast指针遇到空格或者遍历到字符串末尾,就证明遍历完一个单词了
while (fast<chars.length && chars[fast] !=' '){
chars[slow]=chars[fast];
fast++;
slow++;
}
}
}
//相当于C++里面的resize()
char[] newChars=new char[slow];
System.arraycopy(chars,0,newChars,0,slow);
return newChars;
}
//反转整个字符串 双指针实现
public static void reverse(char[] chars,int left,int right){
if (right>=chars.length){
System.out.println("set a wrong right");
return;
}
while (left<right){
char temp=chars[left];
chars[left]=chars[right];
chars[right]=temp;
left++;
right--;
}
}
//反转每个单词
public void reverseEachWord(char[] chars) {
int start = 0;
//end <= s.length() 这里的 = ,是为了让 end 永远指向单词末尾后一个位置,这样 reverse 的实参更好设置
for (int end = 0; end <= chars.length; end++) {
// end 每次到单词末尾后的空格或串尾,开始反转单词
if (end == chars.length || chars[end] == ' ') {
reverse(chars, start, end - 1);
start = end + 1;
}
}
}
}
5.【剑指Offer】LCR182.动态口令
5.1 题目描述
某公司门禁密码使用动态口令技术。初始密码为字符串 password,密码更新均遵循以下步骤:
- 设定一个正整数目标值 target
- 将 password 前 target 个字符按原顺序移动至字符串末尾
请返回更新后的密码字符串。
示例 1:
输入: password = “s3cur1tyC0d3”, target = 4
输出: “r1tyC0d3s3cu”
示例 2:
输入: password = “lrloseumgh”, target = 6
输出: “umghlrlose”
5.2 解题思路
5.3 java代码实现
class Solution {
public String dynamicPassword(String password, int target) {
int len=password.length();
StringBuilder sb=new StringBuilder(password);
reverseString(sb,0,target-1);
reverseString(sb,target,len-1);
return sb.reverse().toString();
}
public void reverseString(StringBuilder sb,int start,int end){
while (start<end){
char temp=sb.charAt(start);
sb.setCharAt(start,sb.charAt(end));
sb.setCharAt(end,temp);
start++;
end--;
}
}
}