[机试]第四章 字符串

例题4.1 特殊乘法

KY18 特殊乘法

#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <iostream>
#include <string>

using namespace std;

int main() {
	string str1, str2;
	while (cin >> str1 >> str2) {
		int result = 0;
		for (int i = 0; i < str1.size(); ++i) {
			for (int j = 0; j < str2.size(); ++j) {
				result += (str1[i] - '0') * (str2[j] - '0');
			}
		}
		printf("%d\n", result);
	}
	return 0;
}

例题4.2 密码翻译

KY33 密码翻译

#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <iostream>
#include <string>

using namespace std;

int main() {
	string str;
	while (getline(cin, str)) {
		for (int i = 0; i < str.size(); ++i) {
			if ((str[i] >= 'a' && str[i] < 'z') || (str[i] >= 'A' && str[i] < 'Z')) {
				str[i] = str[i] + 1;
			}else if (str[i] == 'z') { // 可以换成 str[i] -= 25;
				str[i] = 'a';
			}else if (str[i] == 'Z') {
				str[i] = 'A';
			}
			else {
				str[i] = str[i]; // 可以不写
			}
		}
		cout << str << endl;
	}
	return 0;
}

例题4.3 简单密码

KY90 简单密码

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <string>

using namespace std;

int main() {
	string start, str, end;
	while (getline(cin, start)) {
		if (start == "ENDOFINPUT") {
			break;
		}
		getline(cin, str);
		for (int i = 0; i < str.size(); ++i) { // 也可以用 str[i] = (str[i] - 'A' - 5 + 26) % 26 + 'A';
			if (str[i] >= 'F' && str[i] <= 'Z') {
				str[i] -= 5;
			}
			else if (str[i] == 'A' || str[i] == 'B' || str[i] == 'C' || str[i] == 'D' || str[i] == 'E') {
				str[i] += 21;
			}
		}
		getline(cin, end);
		cout << str << endl;
	}
	return 0;
}

例题4.4 统计字符

KY127 统计字符

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstdio>
#include <string>

using namespace std;

int count[5]; 

int main() {
	string ch, str;
	while (getline(cin, ch)) {
		if (ch == "#") {
			break;
		}
		getline(cin, str);
		int count[5] = {0}; // 也可以用memset(count, 0, sizeof(count))来初始化数组
		for (int i = 0; i < ch.size(); ++i) {
			for (int j = 0; j < str.size(); ++j) {
				if (ch[i] == str[j]) {
					count[i] ++; //也可以维护一个长度为128的数组,将所有字符串的计数都统计一遍
				}
			}
		}
		for (int i = 0; i < ch.size(); ++i) {
			printf("%c %d\n", ch[i], count[i]);
		}
	}
	return 0;
}

例题4.5 字母统计

KY113 字母统计

#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <iostream>
#include <string>

using namespace std;

int main() {
	string str;
	while(getline(cin, str)){
		int count[26] = { 0 };
		for (int i = 0; i < str.size(); ++i) {
			if (str[i] >= 'A' && str[i] <= 'Z') {
				count[str[i] - 'A'] ++;
			}
		}
		for (int i = 0; i < 26; ++i) {
			printf("%c:%d\n", i + 'A', count[i]);
		}
	}
	return 0;
}

习题4.1 skew数

KY45 skew数

#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <iostream>
#include <string>
#include <cmath>

using namespace std;

int main() {
	string str;
	while (getline(cin, str)) {
		int result = 0, k = 0; // 最大整数为2的32次方-1,所以直接用int存结果就可以
		for (int i = str.size() - 1; i >= 0; --i) {
			result += (str[i] - '0') * (pow(2, k + 1) - 1);
			k++;
		}
		printf("%d\n", result);
	}
	return 0;
}
  • 最大整数为2的32次方-1,所以直接用int存结果就可以

习题4.2 单词替换

KY46 单词替换

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstdio>
#include <string>

using namespace std;

int main() {
	string str, word1, word2;
	while (getline(cin, str)) {
		cin >> word1;
		cin >> word2;
		str = " " + str + " ";
		word1 = " " + word1 + " ";
		word2 = " " + word2 + " ";

 		while (str.find(word1) != string::npos) {
			int pos = str.find(word1);
			str.erase(pos, word1.size());
			str.insert(pos, word2);	
		}

		cout << str.substr(1, str.size() - 2) << endl;
	}
	return 0;
}
  • 被替换单词以及替换单词必须是完整的单词(即前后都为空格),可以将s,a,b前后都加空格(将模式串直接改变为我们想要的样子,而不是去思考怎么限定模式串前后为空格),最后输出注意去掉s前后的空格即可。
  • string头文件里有许多强大的函数,如erase,insert,find,!= string::npos用来写循环等等。

习题4.3 首字母大写

KY57 首字母大写

#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <iostream>
#include <string>

using namespace std;

int main() {
	string s;
	while (getline(cin, s)) {
		if (s[0] >= 'a' && s[0] <= 'z') {
			s[0] -= 32;
		}
		
		for (int i = 1; i < s.length(); ++i) {
			if (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n') {
				if (s[i + 1] >= 'a' && s[i + 1] <= 'z') {
					s[i + 1] -= 32;
				}
			}
		}
		cout << s << endl;
	}
	return 0;
}
  • gets/getchar/scanf总结
方法用法特点
getschar s[100]; while(gets(s)){} // 但是很多oj禁用了getsEOF换行符\n的时候就结束字符串的输入
scanfscanf("%s",a)换行符\n表示结束输入,遇到空白符停止
getcharwhile(true) { c = getchar();if (c == '\n') break;s[i++] = c; }可以接受任何字符,可以把’\n’读入
getlinestring str; getline(cin, str)换行符\n表示结束输入,遇到空白符不停止
  • 小写字母-32转化为大写
  • c头文件,strlen()返回字符串长度

习题4.4 浮点数加法(有空再看看)

KY79 浮点数加法

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstdio>
#include <string>

using namespace std;
int main() {
    string f1, f2;
    while (getline(cin, f1)) {
        getline(cin, f2);
        // 小数点对齐
        int pos1 = f1.find('.');
        int pos2 = f2.find('.');
        if (pos1 > pos2) {
            f2.insert(0, pos1 - pos2, '0');
        }
        else {
            f1.insert(0, pos2 - pos1, '0');
        }
        
        // 尾部补零
        if (f1.length() > f2.length()) {
            f2.insert(f2.length(), f1.length() - f2.length(), '0');
        }
        else {
            f1.insert(f1.length(), f2.length() - f1.length(), '0');
        }

        // 从最低位开始模拟字符串加法
        int a = 0; 
        for (int i = f1.length() - 1; i >= 0; --i) {
            if (f1[i] == '.')  continue;
            f1[i] += f2[i] - '0' + a;
            a = (f1[i] - '0') / 10;
            f1[i] = (f1[i] - '0') % 10 + '0';
        }
        if (a != 0) {
            f1.insert(0, 1, (char)a);// 有人这里写'1',不知道为什么
        }
        cout << f1 << endl;
    }
    return 0;
}
  • 字符串加法:设定一个进位数a,当前结果等于字符串相加+上一位的进位a,当前进位a=当前相加结果/10,当前位数值等于当前相加结果%10

习题4.5 后缀子串排序

KY115 后缀子串排序

#define _CRT_SECURE_NO_DEPRECATE
#include <string>
#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

int main() {
	string str;
	string subs[100];
	getline(cin, str);
	for (int i = 0; i < str.size(); ++i) {
		subs[i] = str.substr(i, str.size() - i);
	}
	sort(subs, subs + str.size());
	for (int i = 0; i < str.size(); ++i) {
		cout << subs[i] << endl;
	}
	return 0;
}

例题4.6 Number Sequence

HDU 2007 Spring id1711
Number Sequence id1711
在这里插入图片描述

Input:

2
13 5
1 2 1 2 3 1 2 3 1 3 2 1 2 
1 2 3 1 3
13 5
1 2 1 2 3 1 2 3 1 3 2 1 2
1 2 3 2 1

Output:

6
-1
#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <cstdio>

using namespace std;

const int MAXM = 10000;
const int MAXN = 1000000;

int nextTable[MAXM];
int pattern[MAXM];
int text[MAXN];

// 创建next表:前缀和后缀相同的最大长度
void GetNextTable(int m) {
	int j = 0;
	nextTable[j] = -1;
	int i = nextTable[j];
	while (j < m) {
		if (i == -1 || pattern[j] == pattern[i]) {
			i++;
			j++;
			nextTable[j] = i;
		}
		else {
			i = nextTable[i];
		}
	}
	return ;
}

int KMP(int n, int m) {
	GetNextTable(m);
	int i = 0; // i是text的匹配指针
	int j = 0; // j是pattern的匹配指针
	while (i < n && j < m) {
		if (j == -1 || text[i] == pattern[j]) {
			i++;
			j++;
		}
		else {
			j = nextTable[j];
		}
	}
	if (j == m) {
		return i - j + 1;
	}
	else {
		return -1;
	}
}

int main() {
	int num;
	scanf("%d", &num);
	while (num--) {
		int n, m;
		scanf("%d %d", &n, &m);
		for (int i = 0; i < n; ++i) {
			scanf("%d", &text[i]);
		}
		for (int i = 0; i < m; ++i) {
			scanf("%d", &pattern[i]);
		}
		printf("%d\n", KMP(n, m));
	}
	return 0;
}

杭电oj用c++编译会出错,g++就没问题,迷。

  • 字符串匹配:判断模式串pattern是否为文本串text的子串,如果是,返回模式串出现的最小index,如果不是,返回-1。
  • 利用暴力匹配(一个i“指针”遍历text串,一个j“指针”遍历pattern串,从头到尾挨个对比是否相等)可以得到结果,但是效率不高,没有利用有用的信息跳过一些不可能成功匹配的位置,于是有了KMP算法
  • 在失配之后,模式串具体需要跳过多少个位置?利用next数组来存储:存储在失配后模式串具体跳过多少个位置,计算方法为:字符串前缀和后缀相同的最大长度。

举例:ABABAD

子字符串前缀后缀next(前后缀相同的最大长度)
A-1(没有共有元素,设为特殊值-1,当第一个字符匹配失败时,需要将模式串右移,将p[-1]==*通配符,可以和任意字符匹配,等效实现模式串右移)
ABAB0
ABAA,ABBA,A1
ABABA,AB,ABABAB,AB,B2
ABABAA,AB,ABA,ABABBABA,ABA,BA,A3
ABABADA,AB,ABA,ABAB,ABABABABAD,ABAD,BAD,AD,D0

pattern“ABABAD”的next表如下

PatternIndexNext
*-1N/A
A0-1
B10
A21
B32
A43
D50
  • 利用KMP的匹配过程

举例:
pattern=“BBC ABCDAB ABCDABCDABDE”
text=“ABCDABD”
1.在这里插入图片描述
2.在这里插入图片描述
3.一直重复,直到text有一个字符与pattern的第一个字符符合为止
4.在这里插入图片描述
5.在这里插入图片描述
6.这时候想到的是继续遍历text的下一个字符,重复第1步在这里插入图片描述
7.其实这是很不明智的,因为此时”ABCDAB”已经比较过了,没有必要再做重复的工作,一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是”ABCDAB”。KMP算法的想法是,设法利用这个已知信息,不要把”搜索位置“移回已经比较过的位置,继续把他向后移,这样就提高了效率。
在这里插入图片描述
已知空格与D不匹配时,前面六个字符”ABCDAB”是匹配的。查表可知,最后一个匹配字符B对应的”部分匹配值”为2,因此我们只需要让模式串pattern的下标移动到对应下标为2的位置,也就是C,此时文本串text的下标还是保持不变,在空格处,这样就避免了文本串text下标回溯到第6步了,这样就大大减少了文本串的比较次数。在这里插入图片描述
8.
在这里插入图片描述
9. pattern首字母不匹配,后移一位
在这里插入图片描述
10.
在这里插入图片描述
11.
在这里插入图片描述
12.
在这里插入图片描述

例题4.7 Oulipo

HDU_Oulipo
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

样例输入

3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN

样例输出

1
3
0
#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <string>
#include <cstdio>

using namespace std;

const int MAXM = 10000;
int nextTable[MAXM];

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

int KMP(string text, string pattern) {
	GetNextTable(pattern);
	int n = text.size();
	int m = pattern.size();
	int i = 0;
	int j = 0;
	int number = 0;
	while (i < n) {
		if (j == -1 || text[i] == pattern[j]) {
			i++;
			j++;
		}
		else {
			j = nextTable[j];
		}
		if (j == m) {
			number++;
			j = nextTable[j];
		}
	}
	return number;
}

int main() {
	int casenum;
	scanf("%d", &casenum);
	while (casenum--) {
		string text, pattern;
		cin >> pattern >> text;
		printf("%d\n", KMP(text, pattern));
	}
	return 0;
}

这题妙了,g++超时c++AC 迷惑!

  • 在获取next数组时,把j想象成后缀指针,i是前缀指针,KMP中i是text指针,j是模式串指针。除了i为-1或前后缀相等时,给next数组赋值,剩下的时候都是在改变指针位置~

习题4.6 字符串匹配(有空再看看)

KY186 字符串匹配

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
using namespace std;

const int sub1 = 'a' - 'A', sub2 = 'A' - 'a'; // 为了匹配大小写字母

// 因为模式串内不一定只有一对中括号,所以对每一对中括号进行匹配
bool pipeizhongkuohao(char c, string pat, int left, int right)
// left表示'['下标,rirht表示']'下标
{
    for (int i = left; i <= right; i++) // 这里可以从left+1开始,匹配到right-1结束
    {
        if (c == pat[i] || c == pat[i] + sub1 || c == pat[i] + sub2) return true;
    }
    return false;
}
// 字符串匹配,主串和模式串
int pipei(string str, string pat)
{
    int i = 0, j = 0, k = 0;
    // i表示每次匹配中的主串与匹配位置偏差,j表示模式串偏差,k表示主串匹配开始位置
    while (k < str.size() && k + i < str.size() && j < pat.size())
    {
        // 遇到中括号使用中括号匹配
        if (pat[j] == '[')
        {
            int left = j, right = j + 1; // 左括号位置
            for (right; pat[right] != ']'; right++); // 右括号位置
            bool flag = pipeizhongkuohao(str[k + i], pat, left, right);
            if (flag)
            {
                // 匹配成功,主串偏差值+1,模式串偏差指向右括号下一位置
                i++; j = right + 1;
                if (j == pat.size()) return k; // 结束条件
            }
            else {
                // 匹配失败,主串匹配位置+1,偏差均置0,重新匹配
                k++; i = 0; j = 0;
            }
        }
        else // 字符匹配
        {
            if (str[k + i] == pat[j] || str[k + i] == pat[j] + sub1 || str[k + i] == pat[j] + sub2)
            {
                i++; j++; // 偏差值均+1
                if (j == pat.size())  return k;
            }
            else
            {
                k++; i = 0; j = 0;
            }
        }
    }
    // 匹配失败
    return -1;
}

int main()
{
    int n;
    while (cin >> n)
    {
        string str[n];
        for (int i = 0; i < n; i++)
            cin >> str[i];
        string pattern;
        cin >> pattern;
        for (int i = 0; i < n; i++)
        {
            if (pipei(str[i], pattern) != -1) cout << i + 1 << ' ' << str[i] << endl;
        }
    }
}
  • 普通匹配

习题4.7 String Matching

KY91 String Matching

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <string>

using namespace std;

const int MAXM = 1000000;
int nextTable[MAXM];

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

int KMP(string text, string pattern) {
	GetNextTable(pattern);
	int n = text.size();
	int m = pattern.size();
	int i = 0;
	int j = 0;
	int number = 0;
	while (i < n) {
		if (j == -1 || text[i] == pattern[j]) {
			i++;
			j++;
		}
		else {
			j = nextTable[j];
		}
		if (j == m) {
			number++;
			j = nextTable[j];
		}
	}
	return number;
}
int main() {
	string text, pattern;
	while (cin >> text >> pattern) {
		printf("%d\n", KMP(text, pattern));
	}
	return 0;
}
  • 总结字符串读入
方法用法特点
getschar s[100]; while(gets(s)){} // 但是很多oj禁用了getsEOF换行符\n的时候就结束字符串的输入
scanfscanf("%s",a)换行符\n表示结束输入,遇到空白符停止
getcharwhile(true) { c = getchar();if (c == '\n') break;s[i++] = c; }可以接受任何字符,可以把’\n’读入
getlinestring str; getline(cin, str)换行符\n表示结束输入,遇到空白符不停止
cin遇到空白符(回车、制表、空格、换行)停止
cin.get()cin.get() or ch = cin.get() or cin.get(ch)读取一个任意字符,可实现“按回车键继续”的只读取空白符的操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vanessa Ni

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值