定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部,如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数,要求对长度为n的字符串操作的时间复杂度为O(n),空间复杂度为O(1)。
1.方法一
利用循环置换分解定理
所有序号为 (j+i *m) % n (j 表示每个循环链起始位置,i 为计数变量,m表示左旋转位数,n表示字符串长度),会构成一个循环链(共有gcd(n,m)个,gcd为n、m的最大公约数),每个循环链上的元素只要移动一个位置即可,最后整个过程总共交换了n次(每一次循环链,是交换n/gcd(n,m)次,总共gcd(n,m)个循环链。所以,总共交换n次)。
比如abcd,n=4,m=3
0*3%4=0;
1*3%4=3;
2*3%4=2;
3*3%4=1;
4*3%4=0;
上述的顺序正好跟abcd循环左移的顺序一样。如:
abcd循环移位变成dabc(这里m=3,n=4),ch[0]->temp, ch[3]->ch[0], ch[2]->ch[3],ch[1]->ch[2], temp->ch[1];
当n,m最大公约数大于1时,即有多个循环链,即用j表示。
代码:
#include<iostream>
#include<string>
using namespace std;
int gcd(int m,int n){
if(n==0)
return m;
if(m<n)
gcd(n,m);
else
gcd(n,m%n);
}
void leftReverse(string &str,int k){
int len=str.length();
int cycleNum=gcd(len,k);
int subLen=len/cycleNum;
for(int j=0;j<cycleNum;j++){
char tmp=str[j];
int i;
for(i=0;i<subLen-1;i++){
str[(j+i*k)%len]=str[(j+(i+1)*k)%len];
}
str[(j+i*k)%len]=tmp;
}
}
int main(){
string str="abcd";
leftReverse(str,3);
cout<<str<<endl;
system("pause");
return 0;
}
用一个中间变量接连,使其循环。
方法二
三旋转,(A'B')'=BA
#include<iostream>
#include<string>
using namespace std;
void reverse(string &str){
int len=str.length();
for(int i=0;i<len/2;i++){
char tmp;
tmp=str[i];
str[i]=str[len-1-i];
str[len-1-i]=tmp;
}
}
int main(){
string str="abcde";
int len=str.length();
int k=3;
reverse(str.substr(0,k));
reverse(str.substr(k,len-k));
reverse(str);
cout<<str<<endl;
system("pause");
return 0;
}
发现根本不是想要的结果,原来substr()返回的是一个新的字符串,对它进行操作不会影响原来的str。
#include<iostream>
#include<string>
using namespace std;
void reverse(string &str,int start,int end){//start指开始的下标,end指结束的下标
for(int i=start;i<(end+start)/2;i++){//(end+start)/2中位数
char tmp;
tmp=str[i];
str[i]=str[end-i];
str[end-i]=tmp;
}
}
int main(){
string str="abcde";
int len=str.length();
int k=3;
reverse(str,0,k-1);
reverse(str,k,len-1);
reverse(str,0,len-1);
cout<<str<<endl;
system("pause");
return 0;
}