C语言练习-LeetCode1427字符串左右移

1427. 字符串的左右移

题目

给定一个包含小写英文字母的字符串 s 以及一个矩阵 shift,其中 shift[i] = [direction, amount] direction 可以为 0 (表示左移)或 1 (表示右移)。amount 表示 s 左右移的位数。左移 1 位表示移除 s 的第一个字符,并将该字符插入到 s 的结尾。类似地,右移 1 位表示移除 s 的最后一个字符,并将该字符插入到 s 的开头。对这个字符串进行所有操作后,返回最终结果。

示例 1:

输入:s = “abc”, shift = [[0,1],[1,2]]

输出:“cab”

解释:

[0,1] 表示左移 1 位。 “abc” -> “bca”

[1,2] 表示右移 2 位。 “bca” -> “cab”

示例 2:

输入:s = “abcdefg”, shift = [[1,1],[1,1],[0,2],[1,3]]

输出:“efgabcd”

解释:

[1,1] 表示右移 1 位。 “abcdefg” -> “gabcdef”

[1,1] 表示右移 1 位。 “gabcdef” -> “fgabcde”

[0,2] 表示左移 2 位。 “fgabcde” -> “abcdefg”

[1,3] 表示右移 3 位。 “abcdefg” -> “efgabcd”

提示:
  • 1 <= s.length <= 100 s 只包含小写英文字母

  • 1 <= shift.length <= 100

  • shift[i].length == 2

  • 0 <= shift[i][0] <= 1

  • 0 <= shift[i][1] <= 100

思路

方法一(暴力)

遍历shift数组,判断当前是向左移还是向右移,在根据移动次数,每次移动字符串中的一位。

代码实现
   #include<string.h>
    char* stringShift(char* s, int** shift, int shiftSize, int* shiftColSize) {
        int i = 0;
        int len_s = strlen(s);
        for(i = 0; i < shiftSize; i++){
            //direction表示移动方向,amount表示移动次数
            int direction = shift[i][0];
            int amount = shift[i][1];
    
            //判断是左移还是右移动
            if(0 == direction){    //左移动
                int j = 0;
                for(j = 0; j < amount; j++){    //移动的次数
                    char tmp = s[0];        //保留第一位
                    int k = 0;
                    for(k = 0; k < len_s - 1; k++){    //每次左移一位
                        s[k] = s[k + 1];
                    }
                    s[len_s - 1] = tmp;  //将第一位添加到字符串末尾
                }
            }else{        //右移动
                int j = 0;
                for(j = 0; j < amount; j++){    //移动次数
                    char tmp = s[len_s - 1];    //保留最后一位
                    int k = len_s - 1;
                    for(k = len_s - 1; k > 0 ; k++){    //每次右移一位
                        s[k] = s[k - 1];
                    }
                    s[0] = tmp;    //添加到字符串开头
                }
            }
        }
        return s;
    }
方法二(方法一改进)

方法一中,我们将移动分为了向左移动和向右移动,而这里我们可以将向右移动看成向左移动,只不过是移动次数不在是shift数组中的amount,我们观察可以知道,如果字符串向右移动amount次,可以等效为向左移动len - amount次。不过这里的前提是amount必须小于len所以我们可以进行取余运算这样得到的次数肯定是在0~len当中。

代码实现
    #include<string.h>
    char* stringShift(char* s, int** shift, int shiftSize, int* shiftColSize) {
        int i = 0;
        int len_s = strlen(s);
        for(i = 0; i < shiftSize; i++){
            //direction表示移动方向,amount表示移动次数
            int direction = shift[i][0];
            int amount = shift[i][1];
            amount %= len;
            if(0 == direction){
                amount = amount;
            }else{
                amount = len - amount;
            }
           
            int j = 0;
            for(j = 0; j < amount; j++){    //移动的次数
                char tmp = s[0];        //保留第一位
                int k = 0;
                for(k = 0; k < len_s - 1; k++){    //每次左移一位
                    s[k] = s[k + 1];
                }
                s[len_s - 1] = tmp;  //将第一位添加到字符串末尾
            }
        }
        return s;
    }
方法三

方法二虽然在方法一的基础上在移动方向上进行一定程度改进,使得代码进行简化,然而其时间复杂度还没有改变。这里我们在观察,如果一个字符串abcdef先向左移动 2 次,得到cdefab,在向右移动 1 次,得到bcdefa,在向左移动 2 次得到defabc,这个结果和原字符串abcdef直接向左移动 3 次的结果是一样的,所以,我们在移动之前可以先统计出需要移动的次数,由于在方法二中我们将移动简化为只有向左移动一种情况,那么不需要考虑正负问题。

代码实现
 #include<string.h>
    char* stringShift(char* s, int** shift, int shiftSize, int* shiftColSize) {
        int i = 0;
        int len_s = strlen(s);
        int totalAmount = 0;    //表示总移动次数
        for(i = 0; i < shiftSize; i++){
            //direction表示移动方向,amount表示移动次数
            int direction = shift[i][0];
            int amount = shift[i][1];
            amount %= len_s;
            
            if(1 == direction){    
                amount = len_s - amount;
            }
            totalAmount += amount;
        }
        totalAmount %= len_s;    //取余保证移动次数在 0~len内
        int j = 0;
        for(j = 0; j < totalAmount; j++){    //移动的次数
            char tmp = s[0];        //保留第一位
            int k = 0;
            for(k = 0; k < len_s - 1; k++){    //每次左移一位
                s[k] = s[k + 1];
            }
            s[len_s - 1] = tmp;  //将第一位添加到字符串末尾
        }
        return s;
    }
方法四

在上述方法中,虽然简化了移动方向和移动次数的问题,但是我们在移动字符串时,依旧是每次移动一位,使得效率比较低。如果我们能够事先知道每一个字符需要移动的位置,在借助辅助数组,遍历字符串时将移动后当前位置对应的字符填入即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5QizS5xv-1689827582906)(file:///G:/C_Work/C_item/__notePicture/e5a51f7e-7bab-424e-a726-b8494d22a75d.png?msec=1689826998733)]

代码实现
  #include<string.h>
    char* stringShift(char* s, int** shift, int shiftSize, int* shiftColSize) {
        int i = 0;
        int len_s = strlen(s);
        int totalAmount = 0;    //表示总移动次数
        for(i = 0; i < shiftSize; i++){
            //direction表示移动方向,amount表示移动次数
            int direction = shift[i][0];
            int amount = shift[i][1];
            amount %= len_s;
            
            if(1 == direction){    
                amount = len_s - amount;
            }
            totalAmount += amount;
        }
        totalAmount %= len_s;    //取余保证移动次数在 0~len内
        int j = 0;
        int tmp[len_s];    //辅助数组用于保存S字符串内容
        for(i = 0; i < len_s; i++){
            tmp[i] = s[i];
        }
        for(j = 0; j < len_s; j++){
            s[i] = tmp[(i + amount) % len_s];
        }
        return s;
    }
方法五

观察字符串, 假设有一字符串abcdefg totalAmount = 3,那么移动后字符串defgabc,对比移动前和移动后:

移动前abc | defg

移动后defg | abc

就是将defgabc互换位置得到,再来看一个特殊情况:

移动前ab

移动后ba

ab移动一步得到ba,而反转ab也能得到ba那么是否可以通过反转移动前的字符串来得到移动后的字符串呢

移动前 abc | defg

反转abcdefg得到:cba | gfed

在反转整个字符串得到defg | abc 就得到了移动后的字符串,所以我们可以通过反转字符串来达到移动的效果

代码实现
#include<string.h>
    //反转字符串s中start~end部分[start,end)
    void reverse(char* s,int start,int end){
        end--;
        while(start < end){
            char tmp = s[start];
            s[start] = s[end];
            s[end] = tmp;
            start++;
            end--;
        }
    }
    
    
    char* stringShift(char* s, int** shift, int shiftSize, int* shiftColSize) {
        int i = 0;
        int len_s = strlen(s);
        int totalAmount = 0;    //表示总移动次数
        for(i = 0; i < shiftSize; i++){
            //direction表示移动方向,amount表示移动次数
            int direction = shift[i][0];
            int amount = shift[i][1];
            amount %= len_s;
            
            if(1 == direction){    
                amount = len_s - amount;
            }
            totalAmount += amount;
        }
        totalAmount %= len_s;    //取余保证移动次数在 0~len内
        reverse(s,0,totalAmount);
        reverse(s,totalAmount,len_s);
        reverse(s,0,len_s);
        return s;
    }

若有错误或不足还请指出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值