字符串相关题目

 一、反转字符串

在反转链表中,使用了双指针的方法。

那么反转字符串依然是使用双指针的方法,只不过对于字符串的反转,其实要比链表简单一些。

因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。

对于字符串,我们定义两个指针(也可以说是索引下标),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。

以字符串hello为例,过程如下:

class Solution{
    public void reverseString(char[] s){
        //双指针方法
        int left = 0;
        int right = s.length - 1;
        while(right > left){
            char temp = s[right];
            s[right] = s[left];
            s[left] = temp;
            left++;
            right--;
        }
    }
}

 二、反转字符串||

class Solution{
    public String reverseStr(String s, int k){
       int n = s.length();
       char[] arr = s.toCharArray();
       for(int i = 0; i < n; i += 2 * k){ //i每次移动2k个字符
            reverse(arr, i; Math.min(i + k, n) - 1); //反转2k个字符中前k个字符
        } 
       return new String(arr); 
    }

    private void reverse(char[] arr, int left, int right){
        while(right > left){
            char temp = arr[right];
            arr[right] = arr[left];
            arr[left] = temp;
            left++;
            right--;
        }
    }
}

 三、替换空格

class Solution{
    public String replaceSpace (String s){
        //方法一:定义一个可变长度字符对象,将原字符加入,空格替换为%20
        if(s == null){
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i) == ' '){ //java中的单引号表示字符,双引号是字符串;
//单引号引的数据一般是char类型的;双引号引的数据是String类型的。s.charAt(i) 为 char 类型
                sb.append("%20");
            }else{
                sb.append(sb.append(i));
            }        
        }
        return sb.toString();
    }
}

方法二:双指针 

如果想把这道题目做到极致,就不要只用额外的辅助空间了!

首先扩充数组到每个空格替换成"%20"之后的大小。

然后从后向前替换空格,也就是双指针法,过程如下:

i指向新长度的末尾,j指向旧长度的末尾。

为什么要从后向前填充,从前向后填充不行么?

从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。

其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。

这么做有两个好处:

  1. 不用申请新数组。
  2. 从后向前填充元素,避免了从前先后填充元素要来的 每次添加元素都要将添加元素之后的所有元素向后移动。
class Solution{
    public String replaceSpace(String s){
        if(s == null || s.length() == 0) return s;
        
        //新增长度
        StringBUilder str = new StringBuilder();
        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i) == ' '){
                str.append("  "); //新增两个空格长度
            }
        }
        if(str.length() == 0) return s;//如果没有新增空格,返回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);
    }
}

 四、颠倒字符串中的单词

这里提高一下本题的难度:不要使用辅助空间,空间复杂度要求为O(1)。

不能使用辅助空间之后,那么只能在原字符串上下功夫了。

将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。

所以解题思路如下:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转

举个例子,源字符串为:"the sky is blue "

  • 移除多余空格 : "the sky is blue"
  • 字符串反转:"eulb si yks eht"
  • 单词反转:"blue is sky the"
class Solution{
    public String reverseString(String s){
     //分三步:1.去除字符串中所有的空格;2.翻转整个字符串;3.将单个单词进行翻转
        StringBuilder sb = trimSpace(s);
        reverse(sb, 0 ,sb.length() - 1);
        reverseEachWord(sb);
        return sb.toString();
    }

    //去除空格方法
    private StringBuilder trimSpace(String s){
        int start = 0;
        int end = s.length() - 1;
        while(start < end && s.charAt(start) == ' ') start++; //去除字符串开头空格
        while(Start < end && s.charAt(end) == ' ') end--; //去除字符串尾部空格
        
        StringBuilder sb = new StringBuilder();
        while(start <= end){
            char c = s.charAt(start);
            if(c != ' '){
                sb.append(c);
            }else if(sb.charAt(sb.length() - 1) != ' '){ //已添加到sb中的单词后加一个空格
                sb.append(c);
            } 
            start++;              
        }
        return sb;
    }
    //翻转整个字符串方法
    public void reverse(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 n = sb.length();
        int start = 0;
        int end = 0;
        while(start < n){
            while(end < n && sb.charAt(end) != ' '){
                end++;
            }
            reverse(sb, start, end - 1);
            start = end + 1;
            end ++;
        }
    }
}

 另一种方法,每次处理一个单词,放入可变长度字符对象

class Solution{
    public String reverseString(String s){
        int i = s.length() - 1;
        StringBuilder sb = new StringBuilder();
        while(i > = 0){
            while(i > = 0 && s.charAt(i) == ' ') i--; //找到不为空格的每个单词的第一个索引位置
            int count = 0; //记录每个单词的长度
            while(i > = 0 && s.charAt(i) != ' ')
                i--;
                count++;
            if(count > 0){
                sb.append(s.substring(i + 1, i + count + 1);
                sb.append(' '); //每个单词后添加一个空格
            }
        }
        sb.delete(sb.length() - 1, sb.length());//删除最后一个单词后的空格
        return sb.toString();
    }
}

五、左旋转字符串

为了让本题更有意义,提升一下本题难度:不能申请额外空间,只能在本串上操作

不能使用额外空间的话,模拟在本串操作要实现左旋转字符串的功能还是有点困难的。

可以想一下上一题目使用整体反转+局部反转就可以实现,反转单词顺序的目的。

这道题目也非常类似,依然可以通过局部反转+整体反转 达到左旋转的目的。

具体步骤为:

  1. 反转区间为前n的子串
  2. 反转区间为n到末尾的子串
  3. 反转整个字符串

最后就可以得到左旋n的目的,而不用定义新的字符串,完全在本串上操作。

class Solution{
    public String reverseLeftWords(String s, int n){
        //分为三步操作:1.翻转前n个字符;2.翻转n后的字符;3.翻转整个字符串
        int len = s.length();
        StringBuilder sb = new StringBuilder(s);
        reverseString(sb, 0, n - 1);
        reverseString(sb, n, len - 1);
        reverseString(sb, 0, len - 1);
        return sb.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--;
        }
    }
}

六、实现strStr()

 采用KMP算法,具体实现见KMP算法详解_EvilChou的博客-CSDN博客

七、重复的字符串

  采用KMP算法,具体实现见KMP算法详解_EvilChou的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值