学习July的程序员编程艺术的小结
原文地址:点击打开链接
题目描述:
算法描述:
将一个字符串分成两部分,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,即整个翻转。
代码实现:
#include <iostream>
//字符串反转函数
char* invert(char *start,char *end)
{
char tmp,*ptemp=start;
while(start!=NULL && end!=NULL && start<end)
{
tmp = *start;
*start = *end;
*end = tmp;
start++;
end--;
}
return ptemp;
}
//字符串左旋函数
char* left(char *s,int pos)
{
if(s==NULL)
return s;
int len = strlen(s);
pos = pos % len;
invert(s,s+(pos-1));
invert(s+pos,s+(len-1));
invert(s,s+(len-1));
return s;
}
int main()
{
char s[] = "abcdefghij";
std::cout<<"左旋前:"<<s<<std::endl;
std::cout<<"左旋后:"<<left(s,3)<<std::endl;
return 0;
}
方法二:
算法描述:
所有序号为 (j+i *m) % n (j 表示每个循环链起始位置,i 为计数变量,m表示左旋转位数,n表示字符串长度),
会构成一个循环链(共有gcd(n,m)个,gcd为n、m的最大公约数),每个循环链上的元素只要移动一个位置即可,
最后整个过程总共交换了n次(每一次循环链,是交换n/gcd(n,m)次,总共gcd(n,m)个循环链。所以,总共交换n次)。
通过前面思路的阐述,我们知道对于循环移位,最重要的是指针所指单元不能重复。例如要使abcd循环移位变成dabc(这里m=3,n=4),
经过以下一系列眼花缭乱的赋值过程就可以实现:
ch[0]->temp, ch[3]->ch[0], ch[2]->ch[3], ch[1]->ch[2], temp->ch[1]; (*)
字符串变化为:abcd->_bcd->dbc_->db_c->d_bc->dabc;
代码实现:
#include <iostream>
#include <string>
int gcd(int a,int b)
{
//求最大公约数
int common = a%b;
while(common)
{
a = b;
b = common;
common = a%b;
}
return b;
}
void rotate(std::string& str,int m)
{
int lenOfStr = str.length();
int NumOfGroup = gcd(lenOfStr,m);
int ElementInSub = lenOfStr / NumOfGroup;
for(int j=0;j<NumOfGroup;++j)
{
char tmp = str[j];
int i;
for(i=0;i<ElementInSub-1;++i)
{
str[(j+i*m)%lenOfStr] = str[(j+(i+1)*m)%lenOfStr];
}
str[(j+i*m)%lenOfStr] = tmp;
std::cout<<str<<std::endl;
}
}
int main()
{
std::string str = "abcdefghij";
std::cout<<"左旋前:"<<str<<std::endl;
int m = 5;
rotate(str,m);
std::cout<<"左旋后:"<<str<<std::endl;
return 0;
}
没想到小小的字符串左旋居然存在这么深的问题。特别是这两个算法实在是太神奇了!!!!