您的位置:首页 > 其它

每天学习一算法系列(1)(定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部)

2011-04-20 16:24 399 查看
这几天一直在看了July写了【横空出世,席卷互联网 [评微软等公司数据结构+算法面试100题】,里面的好多算法都很不错,所以决定把看过掌握地并亲自试验一遍没有问题后再写到自己的博客中,当然算法的思想及实现都会注明来自何方。

文章来自:http://blog.csdn.net/v_JULY_v/archive/2011/04/14/6322882.aspx

算法一:左旋转字符串

题目描述:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。

如把字符串abcdef左旋转2位得到字符串cdefab。

请实现字符串左旋转的函数,要求对长度为n的字符串操作的时间复杂度为O(n),空间复杂度为O(1)

思路一:

以下的4点文字分析,引自网友zhedahht(http://zhedahht.blog.163.com/blog/#m)。

分析:

1、如果不考虑时间和空间复杂度的限制,最简单的方法莫过于把这道题看成是把字符串分成前后两部分,通过旋转操作把这两个部分交换位置。

2、于是我们可以新开辟一块长度为n+1的辅助空间,把原字符串后半部分拷贝到新空间的前半部分,在把原字符串的前半部分拷贝到新空间的后半部分。不难看出,这种思路的时间复杂度是O(n),需要的辅助空间也是O(n)。

3、因此,我们另寻思路,咱们试着把字符串看成有两段组成的,记为XY。左旋转相当于要把字符串XY变成YX。

我们先在字符串上定义一种翻转的操作,即翻转字符串中字符的先后顺序:把X翻转后记为XT,显然有(XT)T=X。

我们首先对X和Y两段分别进行翻转操作,这样就能得到XTYT。

接着再对XTYT进行翻转操作,得到(XTYT)T=(YT)T(XT)T=YX。正好是我们期待的结果。

4、分析到这里我们再回到原来的题目:我们要做的仅仅是把字符串分成两段,

第一段为前面m个字符,其余的字符分到第二段。再定义一个翻转字符串的函数,按照前面的步骤翻转三次就行了。时间复杂度和空间复杂度都合乎要求。

void Rotate2(string &str, int nRCnt)
{
int nLen = str.length();
int nNumOfGroup = Gcd(nLen, nRCnt);
int nElemInSub = nLen / nNumOfGroup;

// 整个数组分为nNumOfGroup组,每组有nElemInSub个元素
int i = 0, j = 0;
for(i = 0; i < nNumOfGroup; i++)
{
char cTemp = str[i];

// 每组内循环nElemInSub-1次
for (j = 0; j < nElemInSub - 1; j++)
str[(i + j * nRCnt) % nLen] = str[(i + (j + 1) * nRCnt) % nLen];

str[(i + j * nRCnt) % nLen] = cTemp;
}
}

int _tmain(int argc, _TCHAR* argv[])
{
//char aszStr[] = "Modified by yuucyf";
//LeftRotate(aszStr, 8);
//printf("%s/n",aszStr);

string strStr = "abcdefg";
Rotate2(strStr, 3);
printf("%s/n",strStr.c_str());

return 0;
}


思路三:

//对上述方案4的改写
//④ 所有序号为 (i+t*k) % n (i为指定整数,t为任意整数),....
//copyright@ hplonline && July 2011.04.18。
void my_rotate(int *begin, int *mid, int *end){
int n = end - begin ;
int k = mid - begin ;
int d = __gcd(n, k) ;
int i, j ;
// (i + k * j) % n % d == i % d
for ( i = 0 ; i < d ; i ++ )
{
int tmp = begin[i] ;
int last = i ;
for ( j = (i + k) % n ; j != i ; j++ )
{
begin[last] = begin[j] ;
last = j ;
}
begin[last] = tmp ;
}
}

//前面的代码有问题,多谢zdbill指出.
//下面贴出纠正后的程序.

void my_rotate(char *begin, char *mid, char *end)
{
int n = end - begin;
int k = mid - begin;
int d = gcd(n, k);
int i, j;
for (i = 0; i < d; i ++)
{
int tmp = begin[i];
int last = i;

//i+k为i右移k的位置,%n是当i+k>n时从左重新开始。
for (j = (i + k) % n; j != i; j = (j + k) % n) //多谢zdbill指正。
{
begin[last] = begin[j];
last = j;
}
begin[last] = tmp;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐