zoukankan      html  css  js  c++  java
  • 左旋转字符串

    汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

    思路:三次翻转:假设原数组序列为abcd1234,要求变换成的数组序列为1234abcd,即循环右移了4位。比较之后,不难看出,其中有两段的顺序是不变的:1234和abcd,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。
    变换的过程通过以下步骤完成:
     逆序排列abcd:abcd1234 → dcba1234;
     逆序排列1234:dcba1234 → dcba4321;
     全部逆序:dcba4321 → 1234abcd。

     1 class Solution {
     2 public:
     3     string LeftRotateString(string str, int n) {
     4         int l=str.size();
     5         if(l<n) return str;
     6         reverse(str,0,n-1);
     7         reverse(str,n,l-1);
     8         reverse(str,0,l-1);
     9         return str;
    10     }
    11 private:
    12     void reverse(string &str,int left,int right){
    13         while(left<right){
    14             char c=str[left];
    15             str[left]=str[right];
    16             str[right]=c;
    17             left++;
    18             right--;
    19         }
    20     }
    21 };

    第一节、左旋转字符串
    题目描述:

    定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。
    如把字符串abcdef左旋转2位得到字符串cdefab。
    请实现字符串左旋转的函数,要求对长度为n的字符串操作的时间复杂度为O(n),空间复杂度为O(1) 

    编程之美上有这样一个类似的问题,咱们先来看一下:

    设计一个算法,把一个含有N个元素的数组循环右移K位,要求时间复杂度为O(N),
    且只允许使用两个附加变量。

    分析:

    我们先试验简单的办法,可以每次将数组中的元素右移一位,循环K次。
    abcd1234→4abcd123→34abcd12→234abcd1→1234abcd。
    RightShift(int* arr, int N, int K)
    {
         while(K--)
         {
              int t = arr[N - 1];
              for(int i = N - 1; i > 0; i --)
                   arr[i] = arr[i - 1];
              arr[0] = t;
         }
    }

    虽然这个算法可以实现数组的循环右移,但是算法复杂度为O(K * N),不符合题目的要求,要继续探索。

    假如数组为abcd1234,循环右移4位的话,我们希望到达的状态是1234abcd。
    不妨设K是一个非负的整数,当K为负整数的时候,右移K位,相当于左移(-K)位。
    左移和右移在本质上是一样的。

    解法一:
    大家开始可能会有这样的潜在假设,K<N。事实上,很多时候也的确是这样的。但严格来说,我们不能用这样的“惯性思维”来思考问题。
    尤其在编程的时候,全面地考虑问题是很重要的,K可能是一个远大于N的整数,在这个时候,上面的解法是需要改进的。
    仔细观察循环右移的特点,不难发现:每个元素右移N位后都会回到自己的位置上。因此,如果K > N,右移K-N之后的数组序列跟右移K位的结果是一样的。

    进而可得出一条通用的规律:
    右移K位之后的情形,跟右移K’= K % N位之后的情形一样,如代码清单2-34所示。
    //代码清单2-34
    RightShift(int* arr, int N, int K)
    {
         K %= N;
         while(K--)
         {
              int t = arr[N - 1];
              for(int i = N - 1; i > 0; i --)
                   arr[i] = arr[i - 1];
              arr[0] = t;
         }
    }
    可见,增加考虑循环右移的特点之后,算法复杂度降为O(N^2),这跟K无关,与题目的要求又接近了一步。但时间复杂度还不够低,接下来让我们继续挖掘循环右移前后,数组之间的关联。


    解法二:
    假设原数组序列为abcd1234,要求变换成的数组序列为1234abcd,即循环右移了4位。比较之后,不难看出,其中有两段的顺序是不变的:1234和abcd,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。
    变换的过程通过以下步骤完成:
     逆序排列abcd:abcd1234 → dcba1234;
     逆序排列1234:dcba1234 → dcba4321;
     全部逆序:dcba4321 → 1234abcd。
    伪代码可以参考清单2-35。
    //代码清单2-35
    Reverse(int* arr, int b, int e)
    {
         for(; b < e; b++, e--)
         {
              int temp = arr[e];
              arr[e] = arr[b];
              arr[b] = temp;
         }
    }

    RightShift(int* arr, int N, int k)
    {
         K %= N;
         Reverse(arr, 0, N – K - 1);
         Reverse(arr, N - K, N - 1);
         Reverse(arr, 0, N - 1);
    }

    这样,我们就可以在线性时间内实现右移操作了。

    稍微总结下:
    编程之美上,
    (限制书中思路的根本原因是,题目要求:“且只允许使用两个附加变量”,去掉这个限制,思路便可如泉喷涌)
    1、第一个想法 ,是一个字符一个字符的右移,所以,复杂度为O(N*K)
    2、后来,它改进了,通过这条规律:右移K位之后的情形,跟右移K’= K % N位之后的情形一样
    复杂度为O(N^2)
    3、直到最后,它才提出三次翻转的算法,得到线性复杂度。

    下面,你将看到,本章里我们的做法是:
    1、三次翻转,直接线性
    2、两个指针逐步翻转,线性
    3、stl的rotate算法,线性

    好的,现在,回到咱们的左旋转字符串的问题中来,对于这个左旋转字符串的问题,咱们可以如下这样考虑:
    1.1、思路一:

    对于这个问题,咱们换一个角度可以这么做:
    将一个字符串分成两部分,X和Y两个部分,在字符串上定义反转的操作X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么我们可以得到下面的结论:(X^TY^T)^T=YX。显然我们这就可以转化为字符串的反转的问题了。

    不是么?ok,就拿abcdef 这个例子来说(非常简短的三句,请细看,一看就懂):
    1、首先分为俩部分,X:abc,Y:def;
    2、X->X^T,abc->cba, Y->Y^T,def->fed。
    3、(X^TY^T)^T=YX,cbafed->defabc,即整个翻转。

    我想,这下,你应该了然了。
    然后,代码可以这么写(已测试正确):

      1. //Copyright@ 小桥流水 && July  
      2. //c代码实现,已测试正确。  
      3. //http://www.smallbridge.co.cc/2011/03/13/100%E9%A2%98  
      4. //_21-%E5%B7%A6%E6%97%8B%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2.html  
      5. //July、updated,2011.04.17。  
      6. #include <stdio.h>  
      7. #include <string.h>  
      8. char * invert(char *start, char *end)  
      9. {     
      10. char tmp, *ptmp = start;      
      11. while (start != NULL && end != NULL && start < end)    
      12.     {     
      13.         tmp = *start;     
      14.         *start = *end;        
      15.         *end = tmp;       
      16.         start ++;     
      17.         end --;   
      18.     }  
      19. return ptmp;  
      20. }  
      21. char *left(char *s, int pos)   //pos为要旋转的字符个数,或长度,下面主函数测试中,pos=3。  
      22. {  
      23. int len = strlen(s);  
      24.     invert(s, s + (pos - 1));  //如上,X->X^T,即 abc->cba  
      25.     invert(s + pos, s + (len - 1)); //如上,Y->Y^T,即 def->fed  
      26.     invert(s, s + (len - 1));  //如上,整个翻转,(X^TY^T)^T=YX,即 cbafed->defabc。  
      27. return s;  
      28. }  
      29. int main()  
      30. {     
      31. char s[] = "abcdefghij";      
      32.     puts(left(s, 3));  
      33. return 0;  
      34. }  

    转自:http://blog.csdn.net/zhoushuai520/article/details/7703368

  • 相关阅读:
    $(document).ready() 与$(window).load()
    关于.NET玩爬虫这些事 【初码干货】
    关于c# .net爬虫
    UIScollView Touch事件
    UISearchBar 点击X 按钮收键盘
    IOS7 UITableView一行滑动删除后 被删除行的下一行的点击事件将被忽略解决办法
    IOS 使用dispatch_once 创建单例
    IOS 定位 单例
    IOS拷贝文件到沙盒
    IOS后台运行
  • 原文地址:https://www.cnblogs.com/zl1991/p/4777444.html
Copyright © 2011-2022 走看看