leetcode刷题-字符串

目录

1、Reverse String 反转字符串

2、Reverse String II 反转字符串II

3、Reverse Words in a String 翻转字符串里的单词

4、Find the Index of the FirstOccurrence in a String

实现 strStr()

KMP算法

next数组如何建立

模式串和字符串匹配

5、Repeated Substring Pattern 重复的子字符串

1、Reverse String 反转字符串

Write a function that reverses a string. The input string is given as an array of characters s.

You must do this by modifying the input array in-place with O(1) extra memory.

Example 1:

Input: s = ["h","e","l","l","o"]
Output: ["o","l","l","e","h"]

Example 2:

Input: s = ["H","a","n","n","a","h"]
Output: ["h","a","n","n","a","H"]

Constraints:

class Solution {
    public void reverseString(char[] s) {
        for(int i=0;i<s.length/2;i++){
            char tem=s[i];
            s[i]=s[s.length-i-1];
            s[s.length-i-1]=tem;
        }

    }
}

2、Reverse String II 反转字符串II

Given a string s and an integer k, reverse the first k characters for every 2k characters counting from the start of the string.

If there are fewer than k characters left, reverse all of them. If there are less than 2k but greater than or equal to k characters, then reverse the first k characters and leave the other as original.

Example 1:

Input: s = "abcdefg", k = 2
Output: "bacdfeg"

Example 2:

Input: s = "abcd", k = 2
Output: "bacd"

Constraints:

  • 1 <= s.length <= 10^4
  • s consists of only lowercase English letters.
  • 1 <= k <= 10^4
class Solution {
    public String reverseStr(String s, int k) {
        //自己想的方法,就需要额外判断一下
        // char[] ss=s.toCharArray();
        // int num=s.length()/k;//代表有几个k
        // //这里就包含了奇数个k,存在大于k,小于2k的情况
        // for(int i=0;i<num;i+=2){ //i=0 2 4 6
        //     for(int j=0;j<k/2;j++){
        //     //更换k个字符,记得j一定是从0开始,结束的是k/2,
        //     //因为只需要更换一般,j作用是移动
        //         char tem=ss[k*i+j];//每次交换的起始位置是0k 2k 4k 结束位置是0k+k-1,2k+k-1,4k+k-1
        //         ss[k*i+j]=ss[k*i+k-1-j];//这里面j的作用就用来移动
        //         ss[k*i+k-j-1]=tem;
        //     }
        // }
        // if(num%2==0){//代表有双倍的k,说明剩下的数小于k,需要全部反转
        //     int remain=s.length()%k;//7个
        //     for(int j=0;j<remain/2;j++){//j的作用还是移动,所以从0开始
        //          char tem=ss[s.length()-remain+j];//起始位置为数组总长度前remain个,不断+j
        //          ss[s.length()-remain+j]=ss[s.length()-1-j];//结束位置就是数组最后一个字符,不断-j
        //          ss[s.length()-1-j]=tem;
        //     }
        // }     
        // return new String(ss);
       
       //别人的方法,比较巧妙,也是每隔2k就反转,但是通过end判断够不够k个数据
       //无论数组处于哪种情况,起始位置都是每隔2k的位置开始,然后结束位置就看,
       //假设后面满足k个那就正常反转 不满足k个就按照数组尾部来
       //至于那种大于k,小于2k的也反转了,下一次就从下一个2k开始,但是超过数组长度了,就不会操作
        char[] ch = s.toCharArray();
        for(int i = 0;i < ch.length;i += 2 * k){
            int start = i;//起始位置0k 2k 4k 6k
            // 判断尾数够不够k个来取决end指针的位置
            //这一步很巧妙,如果尾部不够k个,end就为数组最后一个字符
            //针对小于k的情况可以解决
            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);

        // 定义翻转函数,可以复用,减少代码量,i为起始位置,j为结束位置,保障i<j
        // 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;
        // }
  
    }
}

3、Reverse Words in a String 翻转字符串里的单词

Given an input string s, reverse the order of the words.

word is defined as a sequence of non-space characters. The words in s will be separated by at least one space.

Return a string of the words in reverse order concatenated by a single space.

Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces.

Example 1:

Input: s = "the sky is blue"
Output: "blue is sky the"

Example 2:

Input: s = "  hello world  "
Output: "world hello"
Explanation: Your reversed string should not contain leading or trailing spaces.

Example 3:

Input: s = "a good   example"
Output: "example good a"
Explanation: You need to reduce multiple spaces between two words to a single space in the reversed string.

Constraints:

  • 1 <= s.length <= 104
  • s contains English letters (upper-case and lower-case), digits, and spaces ' '.
  • There is at least one word in s.

Follow-up: If the string data type is mutable in your language, can you solve it in-place with O(1) extra space?

class Solution {
    public String reverseWords(String s) {
        StringBuilder sb=new StringBuilder();
        int index=0;

        //这道题目还可以就是全部反转,再把每个单词逐个反转

        //第一种方法:(1)先清除尾部空字符(2)再定位一个单位左右边界,逐个插入新字符串
        // for(int i=s.length()-1;i>=0;i--){
        //     char c=s.charAt(i);
        //     if(c!=' '){
        //         index=i;
        //         break;
        //     }
        // }
        // for(int i=index;i>=0;i--){
        //     int end=i;
        //     while(i>=0&&s.charAt(i)!=' '){
        //         i--;
        //     }
        //     int begin=i;
        //     while(i>=0&&s.charAt(i)==' ')i--;
        //     i++;
            
        //     sb.append(s.substring(begin+1,end+1));
        //     sb.append(' ');
        // }
        // return new String(sb.substring(0,sb.length()-1));
        //第二种方法,直接在一个for循环操作
         for(int i=s.length()-1;i>=0;i--){
            while(i>=0&&s.charAt(i)==' ')i--;//这一步是为了把尾部空字符去掉
            int end=i;//定位单词的结尾
            while(i>=0&&s.charAt(i)!=' '){
                i--;
            }
            int begin=i;//定义单词的开始
            while(i>=0&&s.charAt(i)==' ')i--;//这一步是必须的,保证头部的空格除去
            i++;//这里需要加1
            
            // 可以使用sb.append(s.substring(begin+1,end+1));
            //最好不用substring方法,因为string类的substring方法是产生新得字符串
            for(int j=begin+1;j<end+1;j++){
                sb.append(s.charAt(j));
            }
            sb.append(' ');
           
        }
        return new String(sb.substring(0,sb.length()-1));

        //当然还可以使用char数组,不用substring方法,因为string类的substring方法是产生新得字符串

    }
    
}

4、Find the Index of the FirstOccurrence in a String

实现 strStr()

Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Example 1:

Input: haystack = "sadbutsad", needle = "sad"
Output: 0
Explanation: "sad" occurs at index 0 and 6.
The first occurrence is at index 0, so we return 0.

Example 2:

Input: haystack = "leetcode", needle = "leeto"
Output: -1
Explanation: "leeto" did not occur in "leetcode", so we return -1.

class Solution {
    public int strStr(String haystack, String needle) {
        int[] next=new int[needle.length()];
        int j=0;//记得这里的j代表的就是最长的前后缀长度
        next[0]=0;//next[j]代表包含j之前的最长相等前缀后缀长度
        for(int i=1;i<needle.length();i++){//aabaaf
            while(j>0&&needle.charAt(i)!=needle.charAt(j))
                j=next[j-1];//这里是当不相等时就需要j往后退
            if(needle.charAt(i)==needle.charAt(j)){
                j++;//相等时就代表在原来的最长相等字符串的基础上加1个长度
            }
            next[i]=j;
        }
        j=0;
        for(int i=0;i<haystack.length()&&j<needle.length();i++){//i是一直往前运动的
           
            while(j>0&&haystack.charAt(i)!=needle.charAt(j)){
                j=next[j-1];
            }
            if(haystack.charAt(i)==needle.charAt(j)){
                j++;
            }
            if(j==needle.length()){
                return i-needle.length()+1;
             }
        }
        return -1;

    }
}

KMP算法

关于为什么需要next数组,next数组作用是什么,大家去看其他博主介绍吧

这里主要想讲一下next数组如何建立的 ,还有怎么匹配模式串和字符串

next数组存储的是模式串每个字符的最长前后缀长度,next数组的长度和模式串长度相等

前缀定义:当前字符之前的从第一个字符开始但是不包括最后一个字符的连续字符串

后缀定义:当前字符之前的最后一个字符结束但是不包括第一个字符的连续字符串

例如 模式串aabaaf

next[0]我们就看模式串第一个字符a 前缀有谁,后缀有谁,可以看到前缀后缀是空的,因为前后缀分别不能包含第一个和最后一个字符,所以next[0]=0

next[1],对应就是模式串的aa字符串,前缀只有a  后缀只有a 很明显相同的长度就是只有1个,所以next[1]=1

next[2]对应字符串aab 前缀有 a 和 aa  后缀有ab和b 所以前后缀没有相等的,next[2]=0

next[3]对应aaba 前缀有a 、aa、aab 后缀有 aba、ba、a  所以只有一个a和a相等长度为1 next[1]=1

next[4]对应aabaa 前缀 a  aa  aab  aaba   后缀 a  aa  baa  abaa, 所以相等的最长前后缀字符串为aa 长度为2  next[4]=2 

next数组如何建立

变量i去遍历模式字符串  变量j代表最长前后缀字符串长度 假设模式串数组为p

1、初始化  j=0 next[0]=0  因为第一个字符都是前后缀相等长度为0

假设现在遍历到模式串的第i个字符,那么前面0~i-1个字符的next数组都已经求出了

可以看到,目前假设遍历到i  而next[i-1]=j   也就是那2个红色框

1、观察模式串p[j]和 p[i] 是否相等

(1)如果相等,说明下面的红色框加蓝色框也各自相等

那么模式串从0到i的最长公共前缀不就是j+1吗

所以代码 实现

if(needle.charAt(i)==needle.charAt(j)){
                j++;//相等时就代表在原来的最长相等字符串的基础上加1个长度
           }

(2)如果不相等,就需要回退j  记得我们要找的是最长相等前后缀,所以i是不动的,尾部字符一定包含,头部字符一定包含

那怎么回退j呢 

其实我们就是需要把红色框分别缩短,在2个红色框找到一个最长的前后缀长度,之后再继续比较p[i]和p[j]  也就是找到2个绿色框

因为之前的2个红色框是相等的,所以绿色框相等,那对应位置也会有一个绿色框,如下图,这4个框的长度相同,我们关键就是需要找出绿色框的长度,把j定位到那里去

然后就很巧妙的发现,2个绿色框长度一样,看下图,这不刚好就是next[j-1]吗

因此代码实现就这样,这个过程是循环的,不断的比较p[i]和p[j],直到相等,就回到相等的情况

while(j>0&&needle.charAt(i)!=needle.charAt(j))
                j=next[j-1];//这里是当不相等时就需要j往后退

模式串和字符串匹配

需要2个变量指针i 和 j分别指向模式串和字符串

(1)首先比较字符串和模式串每个字符,当相等的时候,就i++,j++, 记得这里用if就可以了,不需要用while循环,因为本身外面就是以字符串i来遍历的,整个过程中字符串只遍历一次,也就是i一直往前运动的

如果想要用循环也不是不行,就是需要加条件判断i和j的边界,有点麻烦

(2)当遇到不相等的时候,就需要回退j,利用next数组来回退,这个过程就需要循环while

遍历的终止条件就是看模式串的指针j有没有遍历到模式串尾部

for(int i=0;i<haystack.length()&&j<needle.length();i++){//i是一直往前运动的
           
            while(j>0&&haystack.charAt(i)!=needle.charAt(j)){
                j=next[j-1];
            }
            if(haystack.charAt(i)==needle.charAt(j)){
                j++;
            }
            if(j==needle.length()){//终止条件
                return i-needle.length()+1;
             }
        }

5、Repeated Substring Pattern 重复的子字符串

Given a string s, check if it can be constructed by taking a substring of it and appending multiple copies of the substring together.

Example 1:

Input: s = "abab"
Output: true
Explanation: It is the substring "ab" twice.

Example 2:

Input: s = "aba"
Output: false

Example 3:

Input: s = "abcabcabcabc"
Output: true
Explanation: It is the substring "abc" four times or the substring "abcabc" twice.

Constraints:

  • 1 <= s.length <= 104
  • s consists of lowercase English letters
class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int[] next=new int[s.length()];

        //KMP算法,先求出next数组
        int j=0;
        next[0]=0;
        for(int i=1;i<s.length();i++){
            while(j>0&&s.charAt(i)!=s.charAt(j)){
                j=next[j-1];
            }
            if(s.charAt(i)==s.charAt(j))
                j++;
            next[i]=j;
        }
        //假如字符串s由a个重复的子字符串x组成,那么最长相等前后缀肯定是(a-1)个x     xxxxxx
        //所以字符串s的长度减去最长相等前后缀,剩下的就是重复字符串x的长度
        //只需要判断字符串s的长度是不是x长度的倍数就可以了,满足就为true
        if(next[s.length()-1]==0) return false;//这里注意,如果最长前后缀为0直接返回false
        int smallStrlen=s.length()-next[s.length()-1];
        if(s.length()%smallStrlen==0){
            return true;
        }
        return false;

    }
}

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值