今天在翻July的结构算法之道的时候许到一个问题,就是左旋字符串,意思是给了一个字符串"abcdefghijk",如果需要左移三位则变为“defghijkabc”,这个字符串的操作本身很简单:
[我的做法]:我们可以设置两个指针,将字符串分为两段,“abc” ,和”defghijk“,首先对原字符串进行反转变为”kjihgfedcba“,然后我们知道需要左移的位数3,通过指针的操作,从字符串末尾开始反转两个子段,”kjihgfed“,与”cba“,便可以实现这个问题,时间复杂度也为O(n)。
[神奇的递归]:但是在回复内容上看到了另一种解决方法那就是将这个问题转化为一个规模为递归问题,即规模为N的问题化解为规模为M(M<N)的问题,若字符串总长度为L,左侧要旋转的部分长度为s1,那么当从左向右循环交换长度为s1的小段,直到最后,由于剩余的部分长度为s2(s2==L%s1)而不能直接交换。该问题可以递归转化成规模为s1+s2的,方向相反(从右向左)的同一个问题。随着递归的进行,左右反复回荡,直到某一次满足条件L%s1==0而交换结束。
举例来说一下, 还是用”abcdefghijk“这个字符串来说:
1、对于字符串abc def ghi jk,将abc右移到def ghi gk后面,此时n = 11,m = 3,m’ = n % m = 2;
abc def ghi jk -> def ghi abc jk
2、问题变成jk左移到abc前面,此时n = m’ + m = 5,m = 2,m’ = n % m 1;
abc jk -> a jk bc
3、问题变成a右移到jk后面,此时n = m’ + m = 3,m = 1,m’ = n % m = 0;
a jk bc -> jk a bc。
由于此刻,n % m = 0,满足结束条件,返回结果。即从左至右,后从右至左,再从左至右,如此反反复复,直到满足条件,返回退出。
[代码实现如下]:
/*
* =====================================================================================
*
* Filename: string_rotate.cpp
* Version: 1.0
* Created: 2013年10月16日 18时14分00秒
* Revision: none
* Compiler: g++
* Author: szm
*
* =====================================================================================
*/
#include <iostream>
void M_rotate(std::string&str,int n, int m,int head,int tail,bool flag){
if(head==tail||m<=0) //n为字符串原长,m为左旋位数,head,tail,为待处理字符串的头尾指针.
return ;
if(flag==true){
int p1=head; //进行左旋
int p2=head+m;
int k=(n-m)-n%m; //k为移动长度
for(int i=0;i<k;i++,p1++,p2++)
std::swap(str[p1],str[p2]);
M_rotate(str,n-k,n%m,p1,tail,false);
}else{
int p1=tail; //进行右旋
int p2=tail-m;
int k=(n-m)-n%m;
for(int i=0;i<k;i++,p1--,p2--){
std::swap(str[p1],str[p2]);
}
M_rotate(str,n-k,n%m,head,p1,true);
}
}
int main(int argc, char *argv[])
{
std::string str="abcdefghijk";
int i=3;
int len=str.length();
M_rotate(str,len,i%len,0,len-1,true);
std::cout<<"after rotate :"<<str.c_str()<<std::endl;
return 0;
}
对上面的k值说明下,k为移动长度,在上面的M_rotate()函数中,以p2为移动的参照系: n-m 是开始时p2到末尾的长度,n%m是尾巴长度,(n-m)-n%m就是p2移动的距离比如 “abcdefefghi” 开始时 p2指向d, 那么n-m 为defefghi的长度8, n%m 为尾巴hi的长度2, 因为我知道abc要移动到 hi 的前面,所以移动长度是(n-m)-n%m=8-2=6。
上面的思路即是,当字符串总长度为n,左侧要旋转的部分长度为m,那么当从左向右循环交换长度为m的小段直到剩余部分为m’(n % m),此时m’ < m,已不能直接交换了。此后,我们换一个思路,把该问题递归转化成规模大小为m’ +m,方向相反的同一问题。随着递归的进行,直到满足结束条件n %m==0。这种用递归来减小问题规模的方式确实牛逼。