算法基礎——字符串相關(二)

算法基礎——字符串相關(二)

目錄:

  1. 應用實例
    1. 替換空格【劍指Offer_編程題】
    2. 第一個只出現一次的字符位置【劍指Offer_編程題】
    3. 左旋轉字符串【劍指Offer_編程題】
    4. 實現字通配符*【shopee】
    5. 迴文數索引【寒武紀】
    6. 時間轉換【寒武紀】
    7. 最長區間【京東】
    8. 尋找子串【京東】
    9. 迷路的牛牛【網易】
    10. 安置路燈【網易】
    11. 萬萬沒想到之聰明的編輯【字節跳動】
    12. 刪除公共字符【好未來】
    13. 倒置字符串【好未來】
    14. 名字的漂亮度【華爲機試】
    15. 字符串中找出連續最長的數字串【好未來】

一、應用實例:

1、題目描述:請實現一個函數,將一個字符串中的每個空格替換成“%20”。例如,當字符串爲We Are Happy.則經過替換之後的字符串爲We%20Are%20Happy。
void replaceSpace(char *str,int length) {
    //write code here
}【劍指Offer_編程題】

  • 輸入格式:一個字符串
  • 輸出格式:替換空格後的字符串
  • 樣例輸入:
    • We Are Happy
  • 樣例輸出:
    • We%20Are%20Happy

示例代碼:

#include <iostream>
#include <cstring>

using namespace std;

void replaceSpace(char *str,int length) {
	int count = 0;
	for(int i = 0; i < length; i++){
		if(str[i] == ' '){
			count++;
		}
	}
	int index;
	for(int i = length; i >= 0; i--){
		if(count == 0){
			break;
		}
		index = count * 2 + i;
		if(str[i] != ' '){
			str[index] = str[i];
		}else{
			str[index - 2] = '%';
			str[index - 1] = '2';
			str[index] = '0';
			count--;
		}
	}
}

int main(){
	char str[1000];
	while(gets(str)){
		replaceSpace(str, strlen(str));
		for(int i = 0; i < strlen(str); i++){
			cout << str[i];
		}
		cout << endl;
	}
	return 0;
}

2、題目描述:在一個字符串(0<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫).
int FirstNotRepeatingChar(string str) {
    //write code here
}【劍指Offer_編程題】

  • 輸入格式:一個字符串
  • 輸出格式:返回第一個只出現一次的字符的位置,如果沒有則返回-1
  • 樣例輸入:
  • 樣例輸出:

示例代碼:

#include <iostream>
#include <string>
#include <cstring>

using namespace std;

const int MAX_N = 128;

int loc[MAX_N];

int FirstNotRepeatingChar(string str) {
	memset(loc, 0, sizeof(loc));
	for(int i = 0; i < str.size(); i++){
		loc[str[i]]++;
	}
	for(int i = 0; i < str.size(); i++){
		if(loc[str[i]] == 1){
			return i;
		}
	}
	return -1;
}

int main(){
	string s;
	while(getline(cin, s)){
		cout << FirstNotRepeatingChar(s) << endl;
	}
	return 0;
}

3、題目描述:彙編語言中有一種移位指令叫做循環左移(ROL),現在有個簡單的任務,就是用字符串模擬這個指令的運算結果。對於一個給定的字符序列S,請你把其循環左移K位後的序列輸出。例如,字符序列S=”abcXYZdef”,要求輸出循環左移3位後的結果,即“XYZdefabc”。是不是很簡單?OK,搞定它!
string LeftRotateString(string str, int n) {
    //write code here
}【劍指Offer_編程題】

  • 輸入格式:一個字符串
  • 輸出格式:循環左移n位後的字符串
  • 樣例輸入:
    • abcXYZdef
  • 樣例輸出:
    • XYZdefabc

示例代碼:

string LeftRotateString(string str, int n) {
	if(str.size() == 0){
		return str;
	}
	return str.substr(n % str.size()) + str.substr(0, n % str.size());
}

4、題目描述:在Linux Shell命令下通配符'*'表示0個或多個字符, 現編寫一段代碼實現通配符'*'的功能,注意只需要實現'*', 不用實現其他通配符。【shopee】

  • 輸入格式:第一行輸入通配字符串,第二行輸入要匹配查找的字符串
  • 輸出格式:輸出所有匹配的字串起始位置和長度,每行一個匹配輸出。如果不匹配,則輸出 -1 0。如果有多個按照起始位置和長度的正序輸出。
  • 樣例輸入:
    • shopee*.com
    • shopeemobile.com
    • *.com
    • shopeemobile.com
  • 樣例輸出:
    • 0 16
    • 0 16
    • 1 15
    • 2 14
    • 3 13
    • 4 12
    • 5 11
    • 6 10
    • 7 9
    • 8 8
    • 9 7
    • 10 6
    • 11 5
    • 12 4
  • 備註:0 起始位置,16長度

示例代碼:

#include <iostream>
#include <string>
#include <set>

using namespace std;

set<int> result;//從第x個字符結尾的串
string s1, s2;

void DFS(int i, int j){
	if(j == s1.size()){  //當匹配到了正則式的末尾
		result.insert(i);
	}else if(i < s2.size()){
		if(s1[j] == s2[i]){
			DFS(i + 1, j + 1);
		}else if(s1[j] == '*'){
			DFS(i, j + 1);     //*沒匹配到
			DFS(i + 1, j);     //*匹配到了
		}
	}else if(i == s2.size() && s1[j] == '*' && j == s1.size() - 1){
		result.insert(i);
	}
}

int main(){
	while(cin >> s1 >> s2){
		bool flag = false;
		int len;
		for(int i = 0; i < s2.size(); i++){
			if(s1[0] == '*' || s2[i] == s1[0]){
				result.clear();
				DFS(i, 0);
				if(result.size() > 0){
					flag = true;
				}
				for(set<int>::iterator iter = result.begin(); iter != result.end(); iter++){
					len = (*iter) - i;
					if(len > 0){
						cout << i << " " << len << endl;
					}
				}
			}
		}
		if(!flag){
			cout << "-1 0" << endl;
		}
	}
	return 0;
}

5、題目描述:給定一個僅由小寫字母組成的字符串。現在請找出一個位置,刪掉那個字母之後,字符串變成迴文。請放心總會有一個合法的解。如果給定的字符串已經是一個迴文串,那麼輸出-1。【寒武紀】

  • 輸入格式:第一行包含T,測試數據的組數。後面跟有T行,每行包含一個字符串。
  • 輸出格式:如果可以刪去一個字母使它變成迴文串,則輸出任意一個滿足條件的刪去字母的位置(下標從0開始)。例如:bcc,我們可以刪掉位置0的b字符。
  • 樣例輸入:
    • 3
    • aaab
    • baa
    • aaa
  • 樣例輸出:
    • 3
    • 0
    • -1

示例代碼:

#include <iostream>
#include <string>

using namespace std;

bool JudgeStr(string s){
	for(int j = 0; j <= s.size() / 2; j++){
		if(s[j] != s[s.size() - 1 - j]){
			return false;
		}
	}
	return true;
}

int GetLoc(string s){
	if(JudgeStr(s)){
		return -1;
	}
	for(int i = 0; i < s.size(); i++){
		if(JudgeStr(s.substr(0, i) + s.substr(i + 1))){
			return i;
		}
	}
	return -1;
}

int main(){
	string s;
	int n;
	while(cin >> n){
		for(int i = 0; i < n; i++){
			cin >> s;
			cout << GetLoc(s) << endl;
		}
	}
	return 0;
}

6、題目描述:給定一個12小時制的時間,請將其轉換成24小時制的時間。說明:12小時制的午夜12:00:00AM,對應的24小時制時間爲00:00:00。12小時制的中午12:00:00PM,對應的24小時制時間爲12:00:00。【寒武紀】

  • 輸入格式:一個描述12小時制時間的字符串。所有的輸入都是合理的,不用考慮輸入不合理的情況。
  • 輸出格式:一個描述24小時制時間的字符串。
  • 樣例輸入:
    • 08:03:45PM
  • 樣例輸出:
    • 20:03:45

示例代碼:

#include <iostream>
#include <string>

using namespace std;

void print(int index, string s){
	for(int i = index; i < s.size() - 2; i++){
		cout << s[i];
	}
	cout << endl;
}

int main(){
	string s;
	while(cin >> s){
		if(s[8] == 'P'){
			if(s[0] == '1' && s[1] == '2'){
				cout << "12";
			}else{
				cout << (s[0] - '0') * 10 + s[1] - '0' + 12;
			}
			print(2, s);
		}else {
			if(s[0] == '1' && s[1] == '2'){
				cout << "00";
				print(2, s);
			}else{
				print(0, s);
			}
		}
	}
	return 0;
}

7、拉齊有一個01序列,他可以對這個序列進行任意多次變換,每次變換都是把序列的最後若干個元素放到最前面,例如:010011,將最後3個元素011放到最前面,序列變爲011010。所有變換結束後,拉齊需要挑出一個全爲1的連續區間,要求最大化區間長度。【京東】

  • 輸入格式:共一行,一個01串,僅包含0或1。序列長度不超過50000。
  • 輸出格式:一個整數,表示最長區間的長度。
  • 樣例輸入:
    • 11011
  • 樣例輸出:
    • 4

示例代碼1:

#include <string>
#include <iostream>

using namespace std;

int GetMaxSequence(string s){
	int result = 0, tmp = 0;
	int index = 0;
	while(index < s.size()){
		while(index < s.size() && s[index] == '1'){
			tmp++;
			index++;
		}
		result = max(result, tmp);
		tmp = 0;
		index++;
	}
	return result;
}

int main(){
	string s;
	while(cin >> s){
		int result = 0;
		if(s[0] != '1' || s[s.size() - 1] != '1'){
			cout << GetMaxSequence(s) << endl;
		}else{
			int count = 0, index = 0;
			while(index < s.size() && s[index] == '1'){
				count++;
				index++;
			}
			if(index < s.size() - 1){
				index = s.size() - 1;
				while(index >= 0 && s[index] == '1'){
					index--;
					count++;
				}
			}
			count = max(count, GetMaxSequence(s));
			cout << count << endl;
		}
	}
	return 0;
}

示例代碼2:

#include <iostream>
#include <string>

using namespace std;

int main(){
	string s;
	while(cin >> s){
		s = s + s;
		int result = 0, index = 0, tmpCount;
		while(index < s.size()){
			tmpCount = 0;
			while(index < s.size() && s[index] == '1'){
				tmpCount++;
				index++;
			}
			result = max(tmpCount, result);
			index++;
		}
		if(result == s.size()){
			cout << result / 2 << endl;
		}else{
			cout << result << endl;
		}
	}
	return 0;
}

8、題目描述:給出m個字符串S1,S2,...,Sm和一個單獨的字符串T。請在T中選出儘可能多的子串同時滿足:  1)這些子串在T中互不相交。  2)這些子串都是S1,S2,...,Sm中的某個串。問最多能選出多少個子串。【京東】

  • 輸入格式:第一行一個數m(1≤m≤10),接下來m行,每行一個串。最後一行輸入一個串T。輸入中所有單個串的長度不超過100000,串中只會出現小寫字母。
  • 輸出格式:輸出一個數,最多能選出多少串。
  • 樣例輸入:
    • 3
    • aa
    • b
    • ac
    • bbaac
  • 樣例輸出:
    • 3

示例代碼:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

const int MAX_N = 100001;
int nextTable[MAX_N];

struct Node{
	int from;
	int to;
	Node(int f, int t):from(f), to(t){};
};

vector<string> strList;
vector<Node> nodeList;

bool compareAsc(const Node &n1, const Node &n2){
	return n1.to < n2.to;
}

void GetNext(string pattern){
	int j = 0;
	nextTable[j] = -1;
	int i = nextTable[j];
	while(j < pattern.size()){
		if(i == -1 || pattern[j] == pattern[i]){
			i++;
			j++;
			nextTable[j] = i;
		}else{
			i = nextTable[i];
		}
	}
}

void GetNode(string s){
	for(int m = 0; m < strList.size(); m++){
		GetNext(strList[m]);
		int i = 0, j = 0;
		while(i < s.size()){
			if(j == -1 || s[i] == strList[m][j]){
				i++;
				j++;
			}else{
				j = nextTable[j];
			}
			if(j == strList[m].size()){
				nodeList.push_back(Node(i - strList[m].size() + 1, i));
				j = nextTable[j];
			}
		}
	}
}

int main(){
	string s;
	int n;
	while(cin >> n){
		strList.clear();
		nodeList.clear();
		for(int i = 0; i < n; i++){
			cin >> s;
			strList.push_back(s);
		}
		cin >> s;
		GetNode(s);
		sort(nodeList.begin(), nodeList.end(), compareAsc);
		int result = 0, start = 0;
		for(int i = 0; i < nodeList.size(); i++){
			if(nodeList[i].from >= start){
				result++;
				start = nodeList[i].to + 1;
			}
		}
		cout << result << endl;
	}
	return 0;
}

9、題目描述:牛牛去犇犇老師家補課,出門的時候面向北方,但是現在他迷路了。雖然他手裏有一張地圖,但是他需要知道自己面向哪個方向,請你幫幫他。【網易】

  • 輸入格式:每個輸入包含一個測試用例。每個測試用例的第一行包含一個正整數,表示轉方向的次數N(N<=1000)。接下來的一行包含一個長度爲N的字符串,由L和R組成,L表示向左轉,R表示向右轉。
  • 輸出格式:輸出牛牛最後面向的方向,N表示北,S表示南,E表示東,W表示西。
  • 樣例輸入:
    • 3
    • LRR
  • 樣例輸出:
    • E

示例代碼:

#include <iostream>
#include <string>

using namespace std;

const int DIRECTION = 4;

void PrintDirec(int i){
	switch(i){
		case 0:
			cout << "N" << endl;
			break;
		case 1:
			cout << "E" << endl;
			break;
		case 2:
			cout << "S" << endl;
			break;
		case 3:
			cout << "W" << endl;
			break;
	}
}

int main(){
	string s;
	int n;
	while(cin >> n){
		cin >> s;
		int origin = 0;
		for(int i = 0; i < s.size(); i++){
			if(s[i] == 'L'){
				origin = (origin - 1 + 4) % 4;
			}else{
				origin = (origin + 1) % 4;
			}
		}
		PrintDirec(origin);
	}
	return 0;
}

10、題目描述:小Q正在給一條長度爲n的道路設計路燈安置方案。爲了讓問題更簡單,小Q把道路視爲n個方格,需要照亮的地方用'.'表示, 不需要照亮的障礙物格子用'X'表示。小Q現在要在道路上設置一些路燈, 對於安置在pos位置的路燈, 這盞路燈可以照亮pos - 1, pos, pos + 1這三個位置。小Q希望能安置儘量少的路燈照亮所有'.'區域, 希望你能幫他計算一下最少需要多少盞路燈。【網易】

  • 輸入格式:輸入的第一行包含一個正整數t(1 <= t <= 1000), 表示測試用例數接下來每兩行一個測試數據, 第一行一個正整數n(1 <= n <= 1000),表示道路的長度。第二行一個字符串s表示道路的構造,只包含'.'和'X'。
  • 輸出格式:對於每個測試用例, 輸出一個正整數表示最少需要多少盞路燈。
  • 樣例輸入:
    • 2
    • 3
    • .X.
    • 11
    • ...XX....XX
  • 樣例輸出:
    • 1
    • 3

示例代碼:

#include <iostream>
#include <string>

using namespace std;

int main(){
	int n, strLen;
	string s;
	cin >> n;
	for(int m = 0; m < n; m++){
		cin >> strLen >> s;
		int result = 0;
		for(int i = 0; i < strLen; ){
			if(s[i] == '.'){
				result++;
				i += 3;
			}else{
				i++;
			}
		}
		cout << result << endl;
	}
}

11、題目描述:我叫王大錘,是一家出版社的編輯。我負責校對投稿來的英文稿件,這份工作非常煩人,因爲每天都要去修正無數的拼寫錯誤。但是,優秀的人總能在平凡的工作中發現真理。我發現一個發現拼寫錯誤的捷徑:
1. 三個同樣的字母連在一起,一定是拼寫錯誤,去掉一個的就好啦:比如 helllo -> hello
2. 兩對一樣的字母(AABB型)連在一起,一定是拼寫錯誤,去掉第二對的一個字母就好啦:比如 helloo -> hello
3. 上面的規則優先“從左到右”匹配,即如果是AABBCC,雖然AABB和BBCC都是錯誤拼寫,應該優先考慮修復AABB,結果爲AABCC
我特喵是個天才!我在藍翔學過挖掘機和程序設計,按照這個原理寫了一個自動校對器,工作效率從此起飛。用不了多久,我就會出任CEO,當上董事長,迎娶白富美,走上人生巔峯,想想都有點小激動呢!
……
萬萬沒想到,我被開除了,臨走時老闆對我說: “做人做事要兢兢業業、勤勤懇懇、本本分分,人要是行,幹一行行一行。一行行行行行;要是不行,幹一行不行一行,一行不行行行不行。” 我現在整個人紅紅火火恍恍惚惚的……
請聽題:請實現大錘的自動校對程序【字節跳動】

  • 輸入格式:第一行包括一個數字N,表示本次用例包括多少個待校驗的字符串。後面跟隨N行,每行爲一個待校驗的字符串。
  • 輸出格式:N行,每行包括一個被修復後的字符串。
  • 樣例輸入:
    • 2
    • helloo
    • wooooooow
  • 樣例輸出:
    • hello
    • woow

示例代碼:

#include <iostream>
#include <string>

using namespace std;

int main(){
	string s;
	int n;
	while(cin >> n){
		for(int i = 0; i < n; i++){
			cin >> s;
			int index = 0;
			while(index <= s.size()){
				if((index <= s.size() - 3 
					&& s[index] == s[index + 1] 
					&& s[index + 2] == s[index + 1])
				|| (index <= s.size() - 4
					&& s[index] == s[index + 1] 
					&& s[index + 2] == s[index + 3])){
					s = s.substr(0, index + 2) + s.substr(index + 3);
				}else{
					index++;
				}
			}
			cout << s << endl;
		}
	}
	return 0;
}

12、題目描述:輸入兩個字符串,從第一字符串中刪除第二個字符串中所有的字符。例如,輸入They are students.和aeiou,則刪除之後的第一個字符串變成“Thy r stdnts.”【好未來】

  • 輸入格式:每個測試輸入包含2個字符串
  • 輸出格式:輸出刪除後的字符串
  • 樣例輸入:
    • They are students. 
    • aeiou
  • 樣例輸出:
    • Thy r stdnts.

示例代碼:

#include <iostream>
#include <string>

using namespace std;

int main(){
	string s1, s2;
	while(getline(cin, s1)){
		getline(cin, s2);
		string result = "";
		for(int i = 0; i < s1.size(); i++){
			if(s2.find(s1[i]) == s2.npos){
				result += s1[i];
			}
		}
		cout << result;
	}
	return 0;
}

13、題目描述:將一句話的單詞進行倒置,標點不倒置。比如 I like beijing. 經過函數後變爲:beijing. like I【好未來】

  • 輸入格式:每個測試輸入包含1個測試用例: I like beijing. 輸入用例長度不超過100
  • 輸出格式:依次輸出倒置之後的字符串,以空格分割
  • 樣例輸入:
    • I like beijing.
  • 樣例輸出:
    • beijing. like I

示例代碼:

#include <iostream>
#include <stack>
#include <cstring>

using namespace std;

const int MAX_N = 101;

stack<char * > myStack;
char s[MAX_N];

int main(){
	while(gets(s)){
		char *token = strtok(s, " ");
		while(token != NULL){
			myStack.push(token);
			token = strtok(NULL, " ");
		}
		bool flag = false;
		while(!myStack.empty()){
			if(flag){
				cout << " ";
			}
			cout << myStack.top();
			myStack.pop();
			flag = true;
		}
		cout << endl;
	}
	return 0;
}

14、題目描述:給出一個名字,該名字有26個字符串組成,定義這個字符串的“漂亮度”是其所有字母“漂亮度”的總和。每個字母都有一個“漂亮度”,範圍在1到26之間。沒有任何兩個字母擁有相同的“漂亮度”。字母忽略大小寫。給出多個名字,計算每個名字最大可能的“漂亮度”。【華爲機試】

  • 輸入格式:整數N,後續N個名字
  • 輸出格式:每個名稱可能的最大漂亮程度
  • 樣例輸入:
    • 2
    • zhangsan
    • lisi
  • 樣例輸出:
    • 192
    • 101

示例代碼:

#include <iostream>
#include <cctype>
#include <string>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAX_N = 26;

int a[MAX_N];

int main(){
	string s;
	int n;
	while(cin >> n){
		for(int m = 0; m < n; m++){
			memset(a, 0, sizeof(a));
			cin >> s;
			for(int i = 0; i < s.size(); i++){
				int loc = tolower(s[i]) - 'a';
				a[loc]++;
			}
			sort(a, a + MAX_N);
			int sum = 0;
			int factor = MAX_N;
			for(int i = MAX_N - 1; i >= 0 && a[i] != 0; i--){
				sum += a[i] * factor;
				factor--;
			}
			cout << sum << endl;
		}
	}
	return 0;
}

15、題目描述:讀入一個字符串str,輸出字符串str中的連續最長的數字串【好未來】

  • 輸入格式:測試輸入包含1個測試用例,一個字符串str,長度不超過255。
  • 輸出格式:在一行內輸出str中裏連續最長的數字串。
  • 樣例輸入:
    • abcd12345ed125ss123456789
  • 樣例輸出:
    • 123456789

示例代碼:

#include <iostream>
#include <cctype>
#include <string>

using namespace std;

int main(){
	string s;
	while(getline(cin, s)){
		int index = 0;
		string tmp, result = "";
		int maxLen = 0;
		while(index != s.size()){
			if(isdigit(s[index])){
				tmp = "";
				while(index != s.size() && isdigit(s[index])){
					tmp += s[index];
					index++;
				}
				if(tmp.size() > maxLen){
					maxLen = tmp.size();
					result = tmp;
				}
			}else{
				index++;
			}
		}
		cout << result << endl;
	}
	return 0;
}

參考文獻:

[1]楊澤邦、趙霖. 計算機考研——機試指南(第2版). [M]北京:電子工業出版社,2019.11;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章