力扣刷题记录_字符串(自学)

一、字符串

1、反转字符串(力扣344)

	public void reverseString(char[] s) {
        int len = s.length;
        for(int i = 0;i < len / 2;i ++){
            char c = s[i];
            s[i] = s[len - i - 1];
            s[len - i - 1] = c;
        }
    }
	public void reverseString(char[] s) {
        int len = s.length;
        int left = 0,right = len - 1;
        while(left < right){
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp; 
            left ++;
            right --;
        }
    }
//3.位运算
    public void reverseString3(char[] s) {
        int l = 0,r = s.length - 1;
        while(l < r){
            s[l] ^= s[r];
            s[r] ^= s[l];
            s[l] ^= s[r];
            l ++;
            r --;
        }
    }

2、反转字符串 II(力扣541)

	public  String reverseStr(String s, int k) {
        int len = s.length();
        char[] c = s.toCharArray();
        for(int i = 0;i < len;i += 2 * k){
            reverse(c,i,Math.min(i + k,len) - 1);
        }
        return new String(c);
    }
    
	//反转传入的字符数组
    public void reverse(char[] c,int left,int right){
        while(left < right){
            char temp = c[left];
            c[left] = c[right];
            c[right] = temp;
            left ++;
            right --;
        }
    }

3、替换空格(剑指 Offer 05)

//1.使用String类提供的方法
public String replaceSpace(String s) {
	return s.replaceAll(" ","%20");
}
	//2.使用stringbuilder
	public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
        for(int i = 0;i < s.length();i ++){
            char c = s.charAt(i);
            if(c == ' '){
                sb.append("%20");
                continue;
            }
            sb.append(c);
        }
        return sb.toString();
    }
	//3.使用string
	//注意:和第二个方法相比较,这个方法的性能下降许多,后来查了一下,
	// 在《Think in Java》这本书中看到了一章关于字符串的性能说明,
    //1.String对象每执行一次“+”操作都会产生一个新的String对象,
    //2.StringBuilder执行连接操作只有一个对象
	public String replaceSpace(String s) {
        String res = "";
        for(int i = 0;i < s.length();i ++){
            char c = s.charAt(i);
            if(c == ' '){
                res += "%20";
                continue;
            }
            res += c;
        }
        return res;
    }

4、翻转字符串里的单词(力扣151)

	//1.不使用java库函数,自己实现
	public static String reverseWords(String s) {
        //去掉字符串中多余的空格
        StringBuilder sb = trimSpaces(s);
        // 翻转字符串
        reverse(sb,0,sb.length() - 1);
        // 翻转每个单词
        reverseEachWords(sb);

        return sb.toString();
                
    }

    public static StringBuilder trimSpaces(String s){
        int left = 0,right = s.length() - 1;
        // 去掉字符串开头的空白字符
        while(left <= right && s.charAt(left) == ' '){
            left ++;
        }
        // 去掉字符串末尾的空白字符
        while(left <= right && s.charAt(right) == ' '){
            right --;
        }
        // 将字符串间多余的空白字符去除
        StringBuilder sb = new StringBuilder();
        while(left <= right){
            char c = s.charAt(left);
            if(c != ' '){
                sb.append(c);
            }else if(sb.charAt(sb.length() - 1) != ' '){
                sb.append(c);
            }
            left ++;
        }
        return sb;
    }

    public static void reverse(StringBuilder sb, int left, int right) {
        while (left < right) {
            char tmp = sb.charAt(left);
            sb.setCharAt(left++, sb.charAt(right));
            sb.setCharAt(right--, tmp);
        }
    }

    public static void reverseEachWords(StringBuilder sb){
        int len = sb.length();
        int start = 0,end = 0;
        while(start < len){
            while(end < len && sb.charAt(end) != ' '){
                end ++;
            }
            reverse(sb,start,end - 1);
            start = end + 1;
            end ++;
        }
    }
	public String reverseWords(String s) {
        int len = s.length();
        int left = 0,right = len - 1;
        // 去掉字符串开头的空白字符
        while(left <= right && s.charAt(left) == ' '){
            left ++;
        }
        // 去掉字符串末尾的空白字符
        while(left <= right && s.charAt(right) == ' '){
            right --;
        }

        Deque<String> deque = new ArrayDeque<String>();
        StringBuilder sb = new StringBuilder();
        //从前往后遍历,将单词添加到队列的前端
        while(left <= right){
            char c = s.charAt(left);
            if(sb.length() != 0 && c == ' '){
                deque.offerFirst(sb.toString());
                //清空StringBuilder
                sb.setLength(0);
            }else if(c != ' '){
                sb.append(c);
            }
            left ++;
        }
        //不要忘了这一步,当遍历完最后一个单词,直接退出while循环,此时队列中还没有加入最后一个单词
        deque.offerFirst(sb.toString());

        return String.join(" ",deque);
    }

5、左旋转字符串(剑指 Offer 58 - II)

	//	1.字符串切片
	public String reverseLeftWords(String s, int n) {
        return s.substring(n,s.length()) + s.substring(0,n);
    }
	//2.如果面试要求不能使用库函数,利用StringBuilder
	//每轮遍历拼接字符时,只是向列表尾部添加一个新的字符元素。
	//最终拼接转化为字符串时,系统 仅申请一次内存 。
	public String reverseLeftWords(String s, int n) {
        int len = s.length();
        StringBuilder sb = new StringBuilder();
        //先将后面的放入StringBuilder中
        for(int i = n;i < len;i ++){
            sb.append(s.charAt(i));
        }
        //再将前面的放入StringBuilder中
        for(int i = 0;i < n;i ++){
            sb.append(s.charAt(i));
        }

        return sb.toString();
    }
	//3.如果只能使用字符串,构造一个空字符串,依次拼接
	//每轮遍历拼接字符时,都需要新建一个字符串;
	//系统 需申请 N 次内存 ,数据量较大时效率低下。
	String res = "";
        for(int i = n;i < s.length();i ++){
            res += s.charAt(i);
        }
        for(int i = 0;i < n;i ++){
            res += s.charAt(i);
        }
        return res;

利用取余操作,简化代码

	public String reverseLeftWords(String s, int n) {
        StringBuilder sb = new StringBuilder();
        for(int i = n;i < n + s.length();i ++){
            //取余操作,简化代码
            sb.append(s.charAt(i % s.length()));
        }
        return sb.toString();
    }

其实还有一种方法,在4、翻转字符串里的单词中,是先整体反转,再局部反转
本题中,可以先局部反转,再整体反转
例如: abcdefg k=2
局部反转:bagfedc
整体反转:cdefgab

	public String reverseLeftWords(String s, int n) {
        int len = s.length();
        StringBuilder sb = new StringBuilder(s);
        //先局部反转
        reverseString(sb,0,n - 1);
        reverseString(sb,n,len - 1);
        //再整体反转
        return sb.reverse().toString();
    }
    //反转字符串
    public StringBuilder 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 --;
        }
        return sb;
    }

6、实现 strStr()(力扣28)

6.1、实现 strStr()(力扣28)

    //1.利用substring
	public int strStr(String haystack, String needle) {
        if(needle == "") return 0;

        int len1 = haystack.length(),len2 = needle.length();
        for(int i = 0;i < len1 - len2 + 1;i ++){
            if(haystack.substring(i,i + len2).equals(needle)){
                return i;
            }
        }

        return -1;
    }
//2.暴力匹配
    public int strStr2(String haystack, String needle) {
        char[] c1 = haystack.toCharArray();
        char[] c2 = needle.toCharArray();

        int s1Length = haystack.length();
        int s2Length = needle.length();

        int i = 0,j = 0;
        while (i < s1Length && j < s2Length){
            if (c1[i] == c2[j]){
                i ++;
                j ++;
                if (j == s2Length){
                    //返回第一次出现的位置
                    return i - j;
                }
            }else{
                //回到上一次开始比较的下一个位置
                i = i - j  + 1;
                j = 0;
            }
        }

        return -1;
    }

    //3.暴力匹配
    public int strStr3(String haystack, String needle) {
        char[] c1 = haystack.toCharArray();
        char[] c2 = needle.toCharArray();

        int m = c1.length;
        int n = c2.length;

        for(int i = 0;i <= m - n;i ++){
            int index1 = i,index2 = 0;
            while(index2 < n && c1[index1] == c2[index2]){
                index1 ++;
                index2 ++;
            }

            if(index2 == n) return index1 - index2;
        }

        return -1;
    }

6.2、KMP算法

当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。看这里
n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是 O ( n ) O(n) O(n),之前还要单独生成next数组,时间复杂度是 O ( m ) O(m) O(m)。所以整个KMP算法的时间复杂度是 O ( n + m ) O(n+m) O(n+m)的。

//4.KMP算法
    public int strStr4(String s1,String s2){

        if(s2 == " ") return 0;
        int[] next = kmpNext(s2);

        for (int i = 0,j = 0; i < s1.length(); i++) {

            /*
             * s1:文本串   s2:模式串
             * a a b a a b a a f
             *           i
             * 0 1 2 3 4 5
             * a a b a a f
             *           j
             *
             *此时b != f , j 回退到 j == 2,因为知道文本串中有aa和模式串中aa相等,
             *而模式串自己0和1位置的aa和3,4位置的aa相等,所以aa不用再做比较。
             *如果j==2时仍然不相等,接着回退,以此类推...
             *所以用while
             * */
            while (j > 0 && s1.charAt(i) != s2.charAt(j)){
                j = next[j - 1];
            }

            if (s1.charAt(i) == s2.charAt(j)) {
                j ++;
            }

            if (j == s2.length()){
                return i - j + 1;
            }

        }

        return -1;

    }

    //获取一个字符串的部分匹配值表
    public static int[] kmpNext(String s){

        //1.初始化
        int[] next = new int[s.length()];
        next[0] = 0;

        for (int i = 1,j = 0; i < s.length(); i++) {
            /*
             * a b a b a b a f
             *              
             * 0 0 1 2 3 4 5 0
             *
             * 前缀:指不包含最后一个字符的所有以第一个字符开头的连续子串
             * 后缀:指不包含第一个字符的所有以最后一个字符结尾的连续子串
             * */
            while (j > 0 && s.charAt(i) != s.charAt(j)){
                j = next[j - 1];
            }
            //3.前后缀相同
            if (s.charAt(i) == s.charAt(j)){
                j ++;
            }
            //4.填充next数组
            next[i] = j;
        }

        return next;

    }

7、重复的子字符串(力扣459)

	//1.自己想的
    //将s中所有可能的子串全部挑出来,看子串是否能组成s
    public boolean repeatedSubstringPattern(String s) {
        int len = s.length();

        for(int i = 1;i <= len / 2;i ++){
            //做一个优化
            if(len % i != 0) continue;
            StringBuilder sb = new StringBuilder();
            while(sb.length() < len){
                sb.append(s.substring(0,i));
            }
            if(sb.toString().equals(s)){
                return true;
            }
        }
        return false;
    }
	//2.枚举
	public boolean repeatedSubstringPattern(String s) {
        int len = s.length();
        //i是可能的子串的长度
        for(int i = 1;i <= len / 2;i ++){
            if(len % i == 0){
                boolean match = true;
                for(int j = i;j < len;j ++){
                    if(s.charAt(j) != s.charAt(j - i)){
                        match = false;
                        break;
                    }
                }
                if(match){
                    return true;
                }
            }
        }
        return false;
    }
	//3.使用库函数
    public boolean repeatedSubstringPattern3(String s) {
        //indexOf(s,index) 从index下标开始找s首次出现的起始下标
        //如果s = ab ,s + s = abab,起始下标==s.length(),返回false
        //如果s=abab,s + s = abababab,起始下标为2<4,返回true
        //其实就是令s1=s,s2=s,令t=s1+s2,看是否能从t中找到s的第一次出现位置
        //且此位置不为0或s.length(),即看是否能利用s1的后面组合s2的前面得到s
        return (s + s).indexOf(s,1) < s.length();
    }
//4.kmp算法,思路和3一样,只不过查找过程换为kmp算法
    public boolean repeatedSubstringPattern4(String s) {
        String t = s + s;
        return Kmp(t,s) == s.length() ? false : true;
    }

    public int Kmp(String t,String s){
        int[] next = getNext(s);
        for(int i = 1,j = 0;i < t.length();i ++){
            while(j > 0 && t.charAt(i) != s.charAt(j)){
                j = next[j - 1];
            }
            if(t.charAt(i) == s.charAt(j)){
                j ++;
            }
            if(j == s.length()){
                return i - j + 1;
            }
        }
        return -1;
    }

    public int[] getNext(String s){
        int[] next = new int[s.length()];
        next[0] = 0;
        for(int i = 1,j = 0;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;
        }
        return next;
    }
//5.kmp算法
    //5.kmp算法
    public boolean repeatedSubstringPattern5(String s) {
        int len = s.length();
        int[] next = new int[len];
        next[0] = 0;
        for(int i = 1,j = 0;i < len;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;
        }
        //构造出next数组之后,以abab为例,最长公共前后缀的长度是2,4 % (4 - 2) == 0,返回true
        //假设s串是可以由数个子串构成的,那么len - 最长公共前后缀的长度后,就是可构成s的最短子串
        //的长度,最后看len是否是这个最短子串长度的整数倍
        if(next[len - 1] > 0 && len % (len - next[len - 1]) == 0){
            return true;
        }
        return false;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,关于力扣刷C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷中,使用 string 可以方便地处理字符串相关的问。 9. 注意边界条件:在力扣刷中,边界条件往往是解决问的关键。需要仔细分析目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值