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

本题的方法很多,有针对这道题目的解决方法,也有比较通用的思路。

方法1:使用String类库。针对这道题目的特点来解决问题,首先k=k%(s.length());要求将字符串前面的k个字符切割放到字符串的尾巴上面,根据字面意思就可以使用str.subString(k)取出截取后剩余的部分,用str.subString(0,k)取出要截取的部分,然后将他们拼接即可。str.subString(k).append(str.subString(0,k))即为要求的左旋字符串。

publicclass Solution {
    public String LeftRotateString(Stringstr,int n) {
        if(str.length() == 0){
            return str;
        }
        StringBuffer buffer = newStringBuffer(str);
        StringBuffer buffer1 = newStringBuffer(str);
        StringBuffer buffer2 = newStringBuffer();
        buffer.delete(0,n);
        buffer1.delete(n,str.length());
       buffer2.append(buffer.toString()).append(buffer1.toString());
        return buffer2.toString();
    }
}

这是使用Java类库已经封装好的String类和StringbBuffer的类库来实现的,相当于使用Collections.sort()来进行排序,虽然很方便直接,时间复杂度为O(n),空间复杂度为O(n),但是String本质上也是使用char字符数组来解决的,因此,没有从根本上解决问题。

一个常识:subString(int begin)是将[begin开始的后面全部字符串进行截取;subString(intbegin,int end)是截取[begin,end)部分的字符串,即只是截取到end的前面一个字符。

方法2:使用String类库。根据本题的特点,可以发掘一个规律,将str 前面的k个字符放到尾巴上面,只要先将两个str拼接,str=str+str;然后从str中k位置开始截取n个长度即可,例如对于str=abcdef,长度len=6;如果k=3,只要先得到str=abcdefabcdef,然后从k=3开始,截取len=6个字符即为defabc.

classSolution {
public:
    string LeftRotateString(string str, int n){
        int len = str.length();
        if(len == 0) return "";
        n = n % len;
        str += str;
        return str.substr(n, len);
    }
};

方法3:使用可扩展的思路,两次反转字符串。对于abcdef,要求将abc放到后面,可以看做是str1=abc,str2=def这两个字符串要进行反转,即两个单词要进行反转,对于“将句子中的单词进行反转”这个问题,有成熟的方法可以使用,就是进行2次反转来实现,首先将整个句子中的全部字符进行反转,然后对于反转后的句子,对于里面的每个单词进行遍历,使用beginIndex,endIndex进行遍历,当遍历到空格“ ”时就认为是一个单词,将这个单词进行反转,当整个句子中的所有单词都反转之后就得到结果了,即句子中单词之间反转,单词内部顺序,例如”I am a student.”à”student. a am i”;于是关键是创建一个方法将字符数组中从beginIndex开始到endIndex部分的字符进行反转,对于一个字符串进行反转,本身不能完成,需要将其分解成为字符数组再来对数组进行操作,分解成为字符数组之后,可以创建新一个新的数组,将原数组反向遍历放到这个数组中即可;但是最简单高效的方法是,这设置两个指针p1,p2从头尾开始遍历,将p1,p2两个字符进行交换即可,于是时间复杂度是O(n/2),空间复杂度是O(1).

本题中可以将字符串前面k个字符和后面的部分看做是一个句子中的两个单词,要将这两个单词反向,只需要先对整个数组进行反向,然后再对[0,k)和[k,end)这两个子数组进行反向即可。注意这里[0,k)和[k,end)是错误的,由于前一次反转已经导致了整个数组反向,一次此时应该进行反转的子数组的范围是[0,array.length-k)和[array.length-k,end)。

//左旋字符串,使用不依赖String类库的方法来实现,利用反转单词的方法来解决
publicclass Solution {
    public String LeftRotateString(Stringstr,int n) {
       //特殊输入
        if(str==null||str.length()<0) returnnull;
       //空输入;对于空字符串由于length是0可能出错,所以最好特殊处理
        if(str.length()==0) return"";
       //处理n,避免越界
        n=n%(str.length());
        
        //将字符串转变为字符数组
        char[] array=str.toCharArray();
        
       //第一次反转,将整个数组进行反转
        this.reverse(array,0,array.length);
        
       //将[0,k)部分数组进行翻转
        this.reverse(array,0,array.length-n);
        
       //将[k,array.length)部分数组进行翻转
       this.reverse(array,array.length-n,array.length);
        
            //或者return new String(array);
       return String.valueOf(array);
    }
    
   //这个方法专门用来将数组中begin~end部分的数组元素进行翻转
    public void reverse(char[] array,intbegin,int end){
             //已知输入的begin,end一定在array范围内,不会越界
        int pStart=begin;
        int pEnd=end-1;
        while(pStart<pEnd){
            //交换前后对称位置的元素
            char temp=array[pEnd];
            array[pEnd--]=array[pStart];
            array[pStart++]=temp;
        }
    }
}