《程式設計之法》1.1字串的旋轉
題目描述:書中的字串的旋轉其實是指字串的迴圈移動,如將字串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;
}