/** * @file 020_move_string.c * @author dinghuaneng * @date 2011.06.22 * @brief 将字符串进行向左旋转,即循环左移的算法实现。 * 最后那种方法在时间和空间上都很高效,且代码简短,很难出错。 * 最节约空间和时间的方法来源:《编程珠玑》 * @defgroup move_string * @{ */ #include <stdio.h> #include <stdlib.h> #include <string.h> /*********************** 最节约时间的方法 ************************/ /** * @brief 将字符串向左旋转n个位置 * @param str 待旋转的字符串 * @param[in] mov 需要旋转的数量 * @return 无 */ void move_string_left(char *str, int mov) { if (NULL == str || mov <= 0) return; char tmp[mov]; int i; int len = strlen(str); if (len == 0) return; mov %= len; if (mov == 0) return; for (i = 0; i < sizeof tmp; i++) tmp[i] = str[i]; tmp[i] = '\0'; for (i = 0; i < len-mov; i++) str[i] = str[i+mov]; for (; i < len; i++) str[i] = tmp[i-(len-mov)]; } /** * @brief 将字符串向右旋转n个位置 * @param str 待旋转的字符串 * @param[in] mov 需要旋转的数量 * @return 无 */ void move_string_right(char *str, int mov) { if (NULL == str || mov <= 0) return; char tmp[mov]; int i; int len = strlen(str); if (len == 0) return; mov %= len; if (mov == 0) return; for (i = len - mov; i < len; i++) { tmp[i-(len-mov)] = str[i]; } tmp[i-(len-mov)] = '\0'; for (i = len - 1; i >= mov; i--) { str[i] = str[i-mov]; } for (; i >= 0; i--) str[i] = tmp[i]; } /*********************** 最节约时间的方法 ************************/ /*********************** 最节约空间的方法 ************************/ /** * @brief 将字符串向左旋转1个位置 * @param str 待旋转的字符串 * @param[in] mov 需要旋转的数量 * @return 无 */ void move_string_one_left(char *str) { if (NULL == str) return; int len = strlen(str); int i; if (len == 0) return; char tmp = str[0]; for (i=0; i<len-1; i++) { str[i] = str[i+1]; } str[i] = tmp; } /** * @brief 将字符串向右旋转1个位置 * @param str 待旋转的字符串 * @param[in] mov 需要旋转的数量 * @return 无 */ void move_string_one_right(char *str) { if (NULL == str) return; int len = strlen(str); if (len == 0) return; char tmp = str[len-1]; int i; for (i=len-1; i>0; i--) { str[i] = str[i-1]; } str[i] = tmp; } /*********************** 最节约空间的方法 ************************/ /*********************** 最节约空间和时间的方法之一 ************************/ /** * @brief 返回数值i和j的最大公约数 * @return 正确返回最大公约数,参数有问题返回-1 */ int gcd(int i, int j) { if (i<=0 || j<=0) return -1; while (i != j) { if (i > j) i -= j; else j -= i; } return i; } /** * @brief 将字符串向左旋转n个位置 * @param str 待旋转的字符串 * @param[in] mov 需要旋转的数量 * @return 无 */ void move_string_fast_left(char *str, int mov) { if (NULL == str || mov <= 0) return; int len = strlen(str); char tmp; if (!mov) return; mov %= len; if (!mov) return; int i, j, k; int g_cd = gcd(mov, len); for (i=0; i<g_cd; i++) { tmp = str[i]; j = i; while (1) { k = j + mov; if (k >= len) k -= len; if (k == i) break; str[j] = str[k]; j = k; } str[j] = tmp; } } /** * @brief 将字符串向右旋转n个位置 * @param str 待旋转的字符串 * @param[in] mov 需要旋转的数量 * @return 无 */ void move_string_fast_right(char *str, int mov) { if (NULL == str || mov <= 0) return; int len = strlen(str); if (!mov) return; mov %= len; // 修移动次数 if (!mov) return; mov = len - mov; move_string_left(str, mov); } /*********************** 最节约空间和时间的方法之一 ************************/ /*********************** 最节约空间和时间的方法之二 ************************/ /** * @brief 交换字符串str中的从pos1开始和从pos2开始长度为num的两部分元素。 * 注意防止内存越界! * @param str 待交换部分字符的字符串 * @param[in] pos1 第一部分起始位置 * @param[in] pos2 第二部分起始位置 * @param[in] num 要交换的字符数量 */ void swap_string(char *str, int pos1, int pos2, int num) { char *str1 = str + pos1; char *str2 = str + pos2; int i; char tmp; for (i=0; i<num; i++) { tmp = *str1; *str1 = *str2; *str2 = tmp; str1++; str2++; } } /** * @brief 用交换元素的方法进行向左旋转(循环左移) * @param str 待旋转的字符串 * @param[in] mov 需要旋转的数量 * @return 无 */ void move_string_swap_left(char *str, int mov) { if (NULL == str || mov <= 0) return; int len = strlen(str); if (!mov) return; mov %= len; // 修移动次数 if (!mov) return; int i = mov; int j = len - mov; while (i != j) { if (i > j) { swap_string(str, mov-i, mov, j); i -= j; } else { swap_string(str, mov - i, mov - i + j, i); j -= i; } } swap_string(str, mov-i, mov, i); } /*********************** 最节约空间和时间的方法之二 ************************/ /*********************** 最节约空间和时间的方法之三 ************************/ /** * @brief 将字符串str中从start开始至end结束的字符进行逆转 * @param str 待逆转部分字符的字符串 * @param[in] start 开始的位置 * @param[in] end 结束的位置 * @return 无 */ void reverse(char *str, int start, int end) { char *pos1 = str + start; char *pos2 = str + end; char tmp; while (pos1 < pos2) { tmp = *pos1; *pos1 = *pos2; *pos2 = tmp; pos1++; pos2--; } } /** * @brief 利用逆转的方法对字符串进行向左旋转(循环左移) * @param str 待旋转的字符串 * @param[in] mov 需要旋转的数量 * @return 无 */ void move_string_reverse_left(char *str, int mov) { if (NULL == str || mov <= 0) return; int len = strlen(str); if (!mov) return; mov %= len; // 修移动次数 if (!mov) return; reverse(str, 0, mov-1); reverse(str, mov, len-1); reverse(str, 0, len-1); } /*********************** 最节约空间和时间的方法之三 ************************/ /** @} */ #if 1 int main(int argc, char **argv) { char str[] = "Hello World!"; int n = atoi(argv[1]); int i; move_string_reverse_left(str, n); printf("%s\n", str); return 0; } #endif