1. 程式人生 > >《程式設計之法》1.1字串的旋轉

《程式設計之法》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;
}