算法训练营day08|字符串|344.反转字符串,541.反转字符串II,Offer05.替换空格,151.翻转字符串里的单词,Offer58-II.左旋转字符串

1.知识点

1.1异或运算

异或运算也可以理解为无进位相加:相同为0,不同为1

1.1.1性质

  1. 0^N = N N^N=0

  2. 异或运算满足交换律和结合律

    a^b = b^a (a^b)^c = a^(b^c)

  3. 同样一批数,异或结果是一样的

1.1.2应用

交换两个变量的值

假设: int a = 甲 ,int b = 乙

a = a^b; 此时: a = 甲^乙 b=乙

b = a^b; 此时: a = 甲^乙 b=甲^乙^乙=甲

a = a^b; 此时: a = 甲^乙^甲=乙 b=甲

前提是a和b在内存中是两个独立的区域,也就是a和b不能同时指向相同的地址,例如不能自己和自己交换,因为自己和自己异或结果为0.

2.刷题

244.反转字符串

LeetCode链接 344. 反转字符串 - 力扣(LeetCode)

题目描述

方法1:双指针

package daimasuixiangshuati.day08_zifuchuan;

/**
 * @Author LeiGe
 * @Date 2023/10/14
 * @Description todo
 */
public class FanZhuanZiFuChuan344_2 {

    /**
     * 方法1-双指针
     * 直接将数组头尾的元素交换位置即可,直到left=right
     *
     * @param s
     */
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;
        char temp;

        while (left < right) {
            temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
    }
}

时间复杂度:O(N)

空间复杂度:O(1)

方法2:双指针-交换使用位运算

package daimasuixiangshuati.day08_zifuchuan;

/**
 * @Author LeiGe
 * @Date 2023/10/14
 * @Description todo
 */
public class FanZhuanZiFuChuan344_3 {

    /**
     * 方法2-方法1的优化:交换使用位运算
     *
     * @param s
     */
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;
        
        while (left < right) {
            //构造a^b的结果,并放入到a中
            s[left] ^= s[right];
            //s[left] = (char) (s[left] ^ s[right]);

            //将a^b这一结果再^b,存放到b中,此时b=a,a=a^b
            s[right] ^= s[left];
            //s[right] = (char) (s[right] ^ s[left]);

            //a^b的结果再^a,存入a中,此时b=a,a=b,交换完成
            s[left] ^= s[right];
            //s[right] = (char) (s[left] ^ s[right]);

            left++;
            right--;
        }
    }
}

541.反转字符串II

LeetCode链接 541. 反转字符串 II - 力扣(LeetCode)

题目描述

方法1:模拟

package daimasuixiangshuati.day08_zifuchuan;

/**
 * @Author LeiGe
 * @Date 2023/10/22
 * @Description todo
 */
public class FanZhuanZiFuChuanII541_2 {
    /**
     * 方法1-模拟,每遇到2k个字符,就翻转这2k个字符中的前k个
     *
     * @param s
     * @param k
     * @return
     */
    public String reverseStr(String s, int k) {
        char[] chars = s.toCharArray();
        int n = chars.length;
        for (int i = 0; i < n; i += 2 * k) {
            // 如果剩余字符少于k个,将剩余字符全部翻转
            if ((i + k) > n) {
                reverseStrK(chars, i, n - 1);
            }
            // 如果剩余字符大于k个,翻转k个字符
            else {
                reverseStrK(chars, i, i + k - 1);
            }
        }
        return new String(chars);
    }
    
    /**
     * 反转前start-end之间的字符串:双指针,左闭右闭
     *
     * @param s
     * @param start
     * @param end
     */
    private void reverseStrK(char[] s, int start, int end) {
        if (start < 0 || end > s.length - 1) {
            return;
        }
        int left = start;
        int right = end;
        char temp;
        while (left < right) {
            temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
    }
}

时间复杂度:O(N)

空间复杂度:O(1)

剑指Offer05.替换空格

LeetCode链接 LCR 122. 路径加密 - 力扣(LeetCode)

题目描述

方法1:双指针

package daimasuixiangshuati.day08_zifuchuan;

/**
 * @Author LeiGe
 * @Date 2023/10/22
 * @Description todo
 */
public class TiHuanKongGeOffer05_2 {
    /**
     * 方法1:双指针
     * 1.将字符扩充为加上2倍空格的长度,追加到末尾
     * 2.双指针从后往前遍历:left-原来字符串最后一个位置,right-扩充后字符串的最后一个位置
     * 2.1如果chars[left]==' ',将空格替换 chars[right--] = '0';chars[right--] = '2';chars[right--] = '%'; left--;right--;
     * 2.2如果chars[left]!=' ',chars[right]=chars[left] left--;right--;
     *
     * @param s
     * @return
     */
    public String replaceSpace(String s) {
        if (s == null || s.length() == 0) {
            return s;
        }

        //扩充空间,空格数量的2倍数
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' ') {
                str.append(' ');
                str.append(' ');
            }
        }

        // 若没有空格,直接返回
        if (str.length() == 0) {
            return s;
        }

        // 有空格,定义两个指针
        // 左指针,指向原始字符串的最后一个位置
        int left = s.length() - 1;
        // 将空格追加到原始字符串后面
        s += str.toString();
        // 右指针,指向追加空格后的字符串的最后一个位置
        int right = s.length() - 1;

        char[] chars = s.toCharArray();

        while (left >= 0) {
            if (chars[left] == ' ') {
                chars[right--] = '0';
                chars[right--] = '2';
                chars[right--] = '%';
            } else {
                chars[right] = chars[left];
            }
            left--;
            right--;
        }
        return new String(chars);
    }
}

151.翻转字符串里的单词

LeetCode链接 151. 反转字符串中的单词 - 力扣(LeetCode)

题目描述

方法1:翻转2次

package daimasuixiangshuati.day08_zifuchuan;

/**
 * @Author LeiGe
 * @Date 2023/10/22
 * @Description todo
 */
public class FanZhuanZiFuChuanZhongDeDanCi151_2 {
    /**
     * 方法1-将字符串反转两次:
     * 第一次整体反转,第二次反转单词
     * 1.去除首尾以及中间的多余空格
     * 2.反转整个字符串
     * 3.反转各个单词
     *
     * @param s
     * @return
     */
    public String reverseWords(String s) {
        //1.去除首尾以及中间的多余空格
        StringBuilder sb = removeSpace(s);
        //2.反转整个字符串
        reverseString(sb, 0, sb.length() - 1);
        //3.反转各个单词
        reverseEachWord(sb);
        return sb.toString();
    }

    /**
     * 1.去除首尾以及中间的多余空格
     *
     * @param s
     * @return
     */
    private StringBuilder removeSpace(String s) {
        int start = 0;
        int end = s.length() - 1;
        //1.去除两边的空格
        while (s.charAt(start) == ' ') {
            start++;
        }
        while (s.charAt(end) == ' ') {
            end--;
        }
        //2.去除中间的空格
        StringBuilder sb = new StringBuilder();
        while (start <= end) {
            char c = s.charAt(start);
            //当前遍历s的位置不为' '或者sb的最后一位不为' ',则添加当前字符
            if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {
                sb.append(c);
            }
            start++;
        }
        return sb;
    }

    /**
     * 翻转指定区间的字符
     *
     * @param sb
     * @param start
     * @param 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--;
        }
    }

    /**
     * 反转字符串中每一个单词
     *
     * @param sb
     */
    private void reverseEachWord(StringBuilder sb) {
        int n = sb.length();
        int start = 0;//单词反转的开头位置
        int end = 1;//单词反转的结束位置
        while (start < n) {
            while (end < n && sb.charAt(end) != ' ') {
                end++;
            }
            //注意这里的写法,先反转再更新变量,保证了最后一个单词会被反转
            reverseString(sb, start, end - 1);
            start = end + 1;
            end = start + 1;
        }
    }
}

剑指Offer58-II.左旋转字符串

LeetCode链接 LCR 182. 动态口令 - 力扣(LeetCode)

题目描述

方法1:翻转3次

package daimasuixiangshuati.day08_zifuchuan;

/**
 * @Author LeiGe
 * @Date 2023/10/22
 * @Description todo
 */
public class DongTaiKou182_2 {
    /**
     * 方法2:不使用额外空间
     * 1.先反转区间为前n的字串
     * 2.反转区间为n到末尾的字串
     * 3.反转整个字符串
     *
     * @param s
     * @param n
     * @return
     */
    public String reverseLeftWords(String s, int n) {
        char[] chars = s.toCharArray();
        int length = chars.length;
        reverse(chars, 0, n - 1);
        reverse(chars, n, length - 1);
        reverse(chars, 0, length - 1);
        return new String(chars);
    }

    private void reverse(char[] chars, int start, int end) {

        while (start < end) {
            char temp = chars[start];
            chars[start] = chars[end];
            chars[end] = temp;
            start++;
            end--;
        }
    }
}

时间复杂度:O(1)

空间复杂度:O(1)

3.小结

字符串翻转相关的题目,如果一次翻转不能解决问题,可以尝试多次翻转去解决,比如:先整体翻转,后局部翻转,先翻转一部分后翻转一部分等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值