代码随想录-Day8、9 字符串

一、反转字符串

链接: 344.反转字符串

思路

是对于反转字符串最简单的应用

本题字符串的格式是char数组的形式,正常来说,字符串string一般要用string.toCharArray()转化成数组形式

所以只需要简单的遍历,互换位置即可。

代码

class Solution {
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;
        while (left < right){
            char temp = s[right];
            s[right] = s[left];
            s[left] = temp;
            left++;
            right--;
        }
    }
}

二、反转字符串II

链接: 541. 反转字符串II

541.反转字符串II

自己对于由char数组表示的字符串和String字符串之间的一些语法规则不是很熟悉

自己的写法(错误):

class Solution {
    public String reverseStr(String s, int k) {
        for (int i = 0;i < s.length();i += 2*k){
            int start = i;
            int end = Math.min(s.length() - 1,i + k - 1);
            while (start < end){
                char temp;
                temp = s.charAt(start);
                s.charAt(start) = s.charAt(end);
                s.charAt(end) = temp;
                start++;
                end--;
            }
        }
        return s;
    }
}

错误原因:s.charAt()函数只是一个获取String中字符的返回函数,并不能使用该函数修改String中的字符值

正确写法

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;
            int end = Math.min(ch.length - 1,i + k - 1);
            while (start < end){
                char temp;
                temp = ch[start];
                ch[start] = ch[end];
                ch[end] = temp;
                start++;
                end--;
            }
        }
        return new String(ch);
    }
}

在for循环中,i += 2*k的步骤,很巧妙。

字符串知识点

字符串的内容是不会发生改变的,它的内容在创建后不会发生更改。

so在正确写法中,并没有去改变s字符串原来的值,而是将s转化为char[],最后return时创建了一个新的字符串,调用了String(char[] ch)构造方法。

三、替换空格

链接: 剑指Offer 05.替换空格
两种方法,一种方法较为简单,使用空间较大。

一、普通思路方法:

创建一个新的对象,复制str,一边复制一边进行判断,如果为空格,则加入“%20”,否则直接加入该字符即可。

class Solution {
    public String replaceSpace(String s) {
        if (s == null){
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0;i < s.length();i++){
            if (s.charAt(i) == ' '){
                sb.append("%20");
            }else{
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }
}

二、双指针法

思路:

1.将字符串的大小扩展到添加上“%20”的大小

特别注意:

1.1 代码中 大小的扩展s += str.toString(); s中已经有了一个空格的大小,所以在str扩充空格时,每个空格扩充成两个就可以,不能是三个空格。

1.2 String从创建开始不能做任何的改变。所以本题中选用StringBuilder来扩展数组str.append(" ")

1.3 一个空格是字符:’ ’ 两个空格是字符串:" " 字符串的话不能用单引号。

2.找到left指针和right指针的位置:

left为s.length() - 1:未扩充前的

right为扩充后的长度

3.找到循环终止的条件,按逻辑进行执行即可。

class Solution {
    public String replaceSpace(String s) {
        // 创建一个新的对象,根据空格扩展大小
        // 双指针指向
        // 注意:想要改变字符串的值,需要把字符串转化为字符数组,之后以更改过的字符数组作为参数传入创建的新的字符串
        if (s == null || s.length() == 0)
            return null;
        StringBuilder str = new StringBuilder();
        for (int i = 0;i < s.length();i++){
            if (s.charAt(i) == ' ')
            // 三个空格是字符串,不是字符:错误
            // 空格扩充两倍,因为是在s的基础上加的,两倍的空格加上原来s的空格
                str.append("  ");
        }
        if (str.length() == 0)
            return s;
        if(s == null || s.length() == 0){
        return s;
    }
        int left = s.length() - 1;
        s += str.toString();
        int right = s.length() - 1;
        char[] chs = s.toCharArray();
        while (left >= 0){
            if (chs[left] == ' '){
                chs[right--] = '0';
                chs[right--] = '2';
                chs[right] = '%';
            }else{
                chs[right] = chs[left];
            }
            left--;
            right--;
        
        }
        return new String(chs);
    }
}

二刷时,思路都是正确的,但有些细节仍需注意。

一定要注意:

1.定义的数组一定要一致

char[] chs = s.toCharArray(); 自己总是在之后写成ch[left],少了s

2.字符串的长度用s.length(),一定一定不能忘记括号

3.对于++,–这种操作,要想一想是放到括号外面还是括号里面,不要想当然。

四、翻转字符串里的单词

链接: 151.翻转字符串里的单词

心得

简言之,本道题目我做得非常费劲。

自己很少敲代码,对于多了几重的逻辑有种畏惧,以及混乱。各种报错。

思路

一刷,在这里仅记录一种老师讲解的解法

1.去除额外的空格

2.将字符串反转

3.将每个单独的单词反转

代码

class Solution {
    public String reverseWords(String s) {
        char[] chars = s.toCharArray();
        // 1.移除多余的空格
        chars = removeExtraSpace(chars);
        // 2.将字符串反转
        int start = 0;
        int end = chars.length - 1;
        reverse(chars,start,end);
        // 3.将每个单词反转
        reverseEachWord(chars);
        return new String(chars);
    }
    public char[] removeExtraSpace(char[] chars){
        int slow = 0;
        for (int fast = 0;fast < chars.length;fast++){
            if (chars[fast] != ' '){
                if (slow != 0) 
                    chars[slow++] = ' ';
                while (fast < chars.length && chars[fast] != ' '){
                    chars[slow++] = chars[fast++];
                }
            }
        }
        //这一步不要漏掉,原数组虽然去除了多余空格,但长度没变,所以需要复制到一个新的数组中
        //slow这个参数是指复制元素的个数
        char[] newChars = new char[slow];
        System.arraycopy(chars,0,newChars,0,slow);
        return newChars;
    }
    public void reverse(char[] chars,int start,int end){
        char temp;
        while (start < end){
            temp = chars[start];
            chars[start] = chars[end];
            chars[end] = temp;
            start++;
            end--;
        }
    }
    public void reverseEachWord(char[] chars){
        int start = 0;
        for (int i = 0;i <= chars.length;i++){
            if (i == chars.length || chars[i] == ' ' ) {
                reverse(chars,start,i-1);
                start = i + 1;
            }
        }
    }
}

具体内容到二刷时再写。

二刷时:逻辑大体差不多,还有一些细节需要注意

1.在交换完位置等操作时,自己总是会忘记start++或end–之类的操作,要注意

2.if 和while 的一些条件,自己总是很晕。在大脑中模拟这个过程,边写边思考,是一个试错的过程。

例子:

public void reverseEachWord(char[] chars){
        int start = 0;
        for (int i = 0;i <= chars.length;i++){
            if (i == chars.length || chars[i] == ' ' ) {
                reverse(chars,start,i-1);
                start = i + 1;
            }
        }
    }

在上段代码中,需要反转每一个单词,标志着每一个单词结束的点,除了遍历时chars[i] == ’ ',还有可能遍历到了最末端。而最末端需要在for循环的i上=chars.length; 若i = chars.length,那只有chars[i] == ’ '这一个判断条件就不对,因为chars数组中没有索引为chars.length的元素,所以用 || 来处理。

一步步思考,慢慢来。

3.System.arraycopy()的用法

例子:

arrayCopy( arr1, 2, arr2, 5, 10);

意思是;将arr1数组里从索引为2的元素开始, 复制到数组arr2里的索引为5的位置, 复制的元素个数为10个.

五、左旋转字符串

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

剑指Offer58很简单,主要是细节。确定好传入的参数,在纸上画一画,明确一下。不要想当然!

class Solution {
    public String reverseLeftWords(String s, int n) {
        char[] chs = s.toCharArray();
        reverse(chs,0,chs.length - 1);
        reverse(chs,0,chs.length - n - 1);
        reverse(chs,chs.length - n,chs.length - 1);
        return new String(chs);
    }
    public void reverse(char[] chs,int start,int end){
        char temp;
        while (start < end){
            temp = chs[start];
            chs[start] = chs[end];
            chs[end] = temp;
            start++;
            end--;
        }
    }
}

六、实现strStr()

链接: 28. 实现 strStr()

思路

1.KMP的思想是 当字符串不匹配时,可以知道之前一部分已经匹配的文本内容,可以利用这些信息避免从头开始匹配。

2.如何记录已经匹配的内容是KMP的重点,也是next数组的作用

3.next数组是前缀表

前缀表是用来回退的,它记录了当模式串与主串不匹配时,模式串应该从哪个位置开始匹配

什么是前缀表:记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀
构造next数组和借助next数组进行字符串匹配都分为四步:

  1. 初始化
  2. 当字符串不匹配时
  3. 当字符串匹配时
  4. 更新

代码

class Solution {
    public int strStr(String haystack, String needle) {
        if (needle.length() == 0) return 0;
        int[] next = new int[needle.length()];
        getNext(next,needle);
        int j = 0;
        for (int i = 0;i < haystack.length();i++){
            while (j > 0 && needle.charAt(j) != haystack.charAt(i))
                j = next[j - 1];
            if (haystack.charAt(i) == needle.charAt(j)) j++;
            if (j == needle.length())
                return i - needle.length() + 1;
        }
            return  -1;
     }
    public void getNext(int[] next,String s){
        int j = 0;
        next[0] = 0;
        for (int i = 1;i < s.length();i++){
            while (j > 0 && s.charAt(j) != s.charAt(i))
                j = next[j - 1];
            if (s.charAt(i) == s.charAt(j)) j++;
            next[i] = j;
        }
    }
    
}

很多很多细节要注意。

代码主体里面的 模式串与字符串匹配 和 next数组求解 不是完全一样,for循环中定义的 i 的初始值要注意。

因为next是求最长相等前后缀长度,j = 0 ,那么 i 从 1开始即可; 但是模式串与字符串之间的匹配,当然要从头开始匹配啦。

七、重复的子字符串

链接: 459.重复的子字符串

思路

自己选择的思路是求出该字符串的最长相等前后缀的长度(该长度应该存储在next[s.length() - 1]中),然后字符串的长度len - next[s.length - 1]应该是重复的子字符串的长度。

如果len正好可以整除该数,则说明存在重复子字符串。

不必按照代码随想录给的java解析上面占了一个空格的思路写代码,用前一位的代码一样。

j = next[j - 1]

代码

// 带脑子写代码好吧,复制时忘记把!= 改成==了
// 以一个挑剔的目光看,不要看代码时把自己写代码的逻辑带进去

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int len = s.length();
        int[] next = new int[len];
        next[0] = 0;
        int j = 0;
        for (int i = 1;i < len;i++){
            while (j > 0 && s.charAt(j) != s.charAt(i))
                j = next[j - 1];
            if (s.charAt(j) == s.charAt(i)) 
                j++;
            next[i] = j;
        }
        if (next[len - 1] > 0 && len % (len - next[len - 1]) == 0){
            return true;
        }
        return false;
    }
}

自己在复制代码时,总是会出各种各样的错误。每一步带着脑子,不要想当然!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值