您的位置:首页 > 理论基础 > 数据结构算法

数据结构与算法分析笔记与总结(java实现)--字符串2:左旋转字符串

2017-02-15 16:51 253 查看
题目:汇编语言中有一种移位指令叫做循环左移(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;

        }

    }

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐