您的位置:首页 > 编程语言

《编程之法》1.1字符串的旋转

2016-07-14 20:23 309 查看
题目描述:书中的字符串的旋转其实是指字符串的循环移动,如将字符串abcdef向右移动四个字符,变成cdefab

解法一:直接输出

对于字符串abcdef,输出向右移动四个字符后的目的字符串,即依次输出c,d,e,f,a,b,若想直接输出,这意味着遍历原字符串的下标0时,输出原字符串中下标为2的字符c;遍历原字符串下标1时,输出原字符串下标为3的字符d......
若字符串长为n,需向右移动m位,则原下标in与输出下标out的关系为:out = (in + n - m) % n;代码如下。时间复杂度为O(n),空间复杂度为O(0)。

#include <iostream>
using namespace std;
void DirectOutput(char str[], int m, int len){
int i;
for(i = 0; i < len; ++i)
cout << str[(i + len - m) % len];
cout << endl;
}

int main(){
char str[110];
int m;
while(cin >> str >> m){
int len = strlen(str);
DirectOutput(str, m, len);
}
return 0;
}
解法二:蛮力移位  
对于字符串abcdef,建立一个函数MoveOneStep实现字符串向右循环移动一位:先保存str[len-1]到临时变量,为了避免元素覆盖问题,从后往前将s[i-1]的值赋给s[i]。这样进行m次函数调用即可。时间复杂度为O(n*m),空间复杂度为O(1)。

#include <iostream>
using namespace std;
void MoveOneStep(char str[], int len){
int temp = str[len-1], i;
for(i = len-1; i >= 1; --i)
str[i] = str[i-1];
str[i] = temp;
}

int main(){
char str[110];
int m;
while(cin >> str >> m){
int len = strlen(str);
while(m--)
MoveOneStep(str, len);
int i;
for(i = 0; i < len; ++i)
cout << str[i];
cout << endl;
}
return 0;
}
解法三:新建数组
新建一个数组,将原字符串中的元素放在输出字符串对应的位置,如字符串asdfgh需向右移动4位,则新建一个字符数组ans[],将下标为0的字符放到ans[4],下标为1的字符放到ans[5]......则原下标in与目的下标out的关系为:out = (in + m) % n。时间复杂度为O(n),空间复杂度为O(n)。

#include <iostream>
using namespace std;
void UsingArray(char str[], char ans[], int len, int m){
int i;
for(i = 0; i < len; i++)
ans[(i + m) % len] = str[i];
}

int main(){
char str[110], ans[110];
int m;
while(cin >> str >> m){
int len = strlen(str);
memset(ans, 0, sizeof(ans));
UsingArray(str, ans, len, m);
int i;
for(i = 0; i < len; ++i)
cout << ans[i];
cout << endl;
}
return 0;
}
解法四:三步反转
一个规律:如将一个长度为7的字符串asdfghj向右移动4位,变为fghjasd。先对前n-m个字符所在的字符串(下标为0~n-m-1)反转,即变为dsa,再对后m个字符所在的字符串(下标为n-m~n-1的)进行反转,即变为jhgf,最后对整个字符串dsajhgf进行反转变为fghjasd。时间复杂度为O(n),空间复杂度为O(1)。

理解:若向右移动m位,那么原字符串的第n-m个字符会移到目标字符串的最后一个位置,即前n-m个字符整体移到了后面,后m个字符整体移到了前面。前两个小旋转使两个整体分段逆序,后面一个大旋转一方面使两个整体交换位置,另一方面正正得负,保证每个字符经过两次旋转后的位置不会出差错。

#include <iostream>
using namespace std;
void ReverseString(char str[], int start, int end){
int temp;
while(start < end){
temp = str[end];
str[end] = str[start];
str[start] = temp;
--end;
++start;
}
}

int main(){
char str[110];
int m;
while(cin >> str >> m){
int len = strlen(str);
m = m % len;
ReverseString(str, 0, len-m-1);
ReverseString(str, len-m, len-1);
ReverseString(str, 0, len-1);
int i;
for(i = 0; i < len; ++i)
cout << str[i];
cout << endl;
}
return 0;
}


举一反三:
题目描述:单词反转,如输入"I am a student.",输出"student. a am I"。 
解法一:在此为了简便,使用string的获得子串函数string substr(int pos = 0, int n = npos) const;,pos表示起始下标,n表示子字符串的字符个数。从后往前遍历数组,以空格为间隔分离子字符串,依次加到字符串ans中。

#include <iostream>
#include <string>
using namespace std;
int main(){
string str, ans;
while(getline(cin, str)){ //不能直接使用cin >> str;否则只会取第一个空格前的内容
int i, cnt;
for(i = str.size()-1; i >= 0; --i){//i >= 0条件针对第一个单词
cnt = 0;
while(i >= 0 && str[i] != ' '){
++cnt;
--i;
}
ans = ans + str.substr(i+1, cnt) + " ";
}
cout << ans.substr(0, str.size()) << endl;//去掉最后一个空格
}
return 0;
}解法二:反转字符串两次a, 先将"I am a student."反转为".tenduts a ma I":设置两个指针指向字符串头和尾并相向移动,然后依次对换字符;b,
设置两个指针i和j,遍历一次数组,分别使他们指向字符串中某一单词的头和尾;c, 根据[i, j]区间,对每个单词进行反转,如反转ma为am,像步骤a一样头尾依次互换。实际上整个步骤需要三次遍历数组,即时间复杂度为O(n) + O(n) + O(n) = O(n),空间复杂度为O(1)。
#include <iostream>
#include <string>
using namespace std;
void Reverse(string &str, int start, int end){
int temp;
while(start < end){
temp = str[start];
str[start++] = str[end];
str[end--] = temp;
}
}
int main(){
string str;
while(getline(cin, str)){ //注意使用getline
Reverse(str, 0, str.size()-1);
int i = 0, j = 0;
while(i < str.size()){
if(i == str.size()-1 || str[i+1] == ' '){//此时i指向某一单词的最后一个位置,利用了||,若第一个条件成立,第二个就不用比较了
Reverse(str, j, i);
j = i + 2;//此时j跳过空格指向某一单词的第一个位置
}
++i;
}
cout << str << endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息