紫薯基础篇 | 3-数组与字符串
字符输入
几种输入函数的返回值及循环条件判断
几种输入函数的返回值及循环条件中如何判断是否输入结束:
scanf()
返回读入字符的个数。
如果遇到错误或者end of file,返回值为EOF,其中EOF值是-1。在计算机中,-1的补码表示为全1,将其按位取反则为0。因此循环条件可以这么写:
int n;
while (~scanf("%d", &n)) {
}
cin>>
cin>>运算符用来从一个istream对象读取输入数据,cin是一个istream对象。cin>>a的含义是从标准输入中读取a,他的返回值是一个“已经读取了a的新流”,然后从这个新流中继续读取下个值。与scanf()类似,当读取不匹配类型的值或EOF时会造成流错误而结束循环。
string s;
while (cin >> s) {
}
getline()
while (getline(cin, s)) {}
返回值与cin>>相同。
getchar()
char c;
while ((c = getchar()) != EOF) {}
getchar() 返回值为 int 型,为用户输入的ASCII码或EOF。
提示3-15:在使用fgetc和getchar时,应该避免写出和操作系统相关的程序。
多个字符输入A - TEX Quotes
解题总结
多个字符输入时,直接运用c = getchar()
函数的返回值。
参考代码
while ((c = getchar()) != EOF) {
if (c == '"') {
printf("%s", flag ? "``" : "''");
flag = !flag;
} else printf("%c", c);
}
输入整行E - Puzzle
注意事项
太坑了!!输入的指令可能是非法的!!
解题总结
1.输入多行字符的方式。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define maxn 10000010
#define INF 0x3f3f3f3f
typedef long long LL;
vector<string> t;
int readint() {
int x; scanf("%d", &x); return x;
}
bool isValid(int x, int y) {
return (x >= 0 && x <= 4 && y >= 0 && y <= 4) ? true : false;
}
bool judge(char c, int &x, int &y) {
bool flag = false;
if (c == 'A') {
if (isValid(x - 1, y)) {
swap(t[x][y], t[x - 1][y]);
x--;
flag = true;
}
} else if (c == 'B') {
if (isValid(x + 1, y)) {
swap(t[x][y], t[x + 1][y]);
x++;
flag = true;
}
} else if (c == 'R') {
if (isValid(x, y + 1)) {
swap(t[x][y], t[x][y + 1]);
y++;
flag = true;
}
} else if (c == 'L') {
//注意不能用else!!指令可能本身就是非法的
if (isValid(x, y - 1)) {
swap(t[x][y], t[x][y - 1]);
y--;
flag = true;
}
}
return flag ? true : false;
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
string s;
int cnt = 0;
while (getline(cin, s) && s[0] != 'Z') {
t.clear();
//t.pb(s);
int line = 0, x, y;
_rep(i, 1, 5) {
t.pb(s);
int pos = s.find(' ');
if (pos != -1) {
x = line;
y = pos;
}
line++;
if (i < 5) getline(cin, s);
}
char c;
bool flag = true;
while ((c = getchar()) && c != '0') {
if (c == '\n') continue;
if (!flag) continue;
if (!judge(c, x, y)) flag = false;
}
getchar();
if (cnt) printf("\n");
printf("Puzzle #%d:\n", ++cnt);
if (!flag) {
printf("This puzzle has no final configuration.\n");
continue;
}
_for(i, 0, 5) {
_for(j, 0, 5) {
if (j) printf(" ");
printf("%c", t[i][j]);
}
printf("\n");
}
}
return 0;
}
打表思想
常量数组B - WERTYU
解题思路
开始的时候想到的是单个字符判断,但这样太麻烦。
开一个常量数组比较方便。
解题总结
善用常量数组往往能简化代码。定义常量数组时无须指明大小,编译器会计算。
参考代码
string s = "`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./";
char c;
while ((c = getchar()) != EOF) {
int pos = s.find(c);
if (pos != -1)
printf("%c", s[s.find(c) - 1]);
else
printf("%c", c);
}
镜像判断C - Palindromes
解题思路
一个是判断回文,一个是判断是否是镜像。
镜像的判断用常量数组比较方便。
解题总结
1.注意题目的输出格式。(题目要求每个样例后面输一个空行)
2.碰到多个输出语句,可以存在一个字符数组里面,写的时候更简洁。
3.注意循环的条件(len
),因为要判断是否是镜像,所以如果是奇数个字符,中间的也要判断。
参考代码
自己写的:
string s;
bool isP, isM;
string rev = "A 3 HIL JM O 2TUVWXY51SE Z 8 ";
while (getline(cin, s)) {
isP = true; isM = true;
int len = s.length();
_for(i, 0, (len + 1) / 2) {
//不能是len,不然中间的字母没有判断镜像
if (s[i] != s[len - i - 1]) isP = false;
if (s[len - i - 1] != (isalpha(s[i]) ? rev[s[i] - 'A'] : rev[s[i] - '1' + 26])) isM = false;
}
if (isP || isM) {
if (isP && isM)
printf("%s -- is a mirrored palindrome.", s.c_str());
else if (isM) {
printf("%s -- is a mirrored string.", s.c_str());
} else {
printf("%s -- is a regular palindrome.", s.c_str());
}
} else
printf("%s -- is not a palindrome.", s.c_str());
printf("\n\n");
}
参考书上的:
输出语句值得借鉴~巧妙利用两个判断变量的值,使得代码很简洁。
常量字符数组的初始化。
#include<bits/stdc++.h>
using namespace std;
//字符常量数组
const char* str[] = {
"not a palindrome", "a palindrome", "a mirrored string", "a mirrored palindrome"};
int main() {
string s;
string rev = "A 3 HIL JM O 2TUVWXY51SE Z 8 ";
int isP, isM;
while (getline(cin, s)) {
isP = 1; isM = 1;
int len = s.length();
_for(i, 0, (len + 1) / 2) {
//不能是len,不然中间的字母没有判断镜像
if (s[i] != s[len - i - 1]) isP = 0;
if (s[len - i - 1] != (isalpha(s[i]) ? rev[s[i] - 'A'] : rev[s[i] - '1' + 26])) isM = 0;
}
printf("%s -- is %s.", s.c_str(), str[isP + isM * 2]); //输出语句很简洁
printf("\n\n");
}
return 0;
}
直接打表E - Digit Generator
解题思路
本题看起来是个数学题,实则不然。假设所求生成元为m。不难发现m<n。换句话说,只需枚举所有的m<n,看看有没有哪个数是n的生成元。
可惜这样做的效率并不高,因为每次计算一个n的生成元都需要枚举n-1个数。
实际上只需一次性枚举100000内的所有正整数m,标记“m加上m的各个数字之和得到的数有一个生成元是m”,最后后查表即可。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define maxn 100010
#define INF 0x3f3f3f3f
int readint() {
int x; scanf("%d", &x); return x;
}
int gen[maxn]; //标记数i的生成数是gen[i]
int main() {
//打表
fill(gen, gen + maxn, INF);
_rep(i, 1, 100000) {
int sum = i;
int num = i;
while (num) {
sum += num % 10;
num /= 10;
}
if (i < gen[sum]) gen[sum] = i;
}
int t, k;
scanf("%d", &t);
while (t--) {
scanf("%d", &k);
printf("%d\n", gen[k] == INF ? 0 : gen[k]);
}
return 0;
}
打表&从输入的字符串中提取数字L - Floating-Point Numbers
解题思路
打表,需要进行数学的递推,防止数据溢出。
真的不会做,参考了别人的题解qwq:https://blog.csdn.net/zhouzi2018/article/details/80717551
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define maxn 310
#define INF 0x3f3f3f3f
typedef long long LL;
typedef double db;
const double eps = 1e-6; //定义浮点数误差
int readint() {
int x; scanf("%d", &x); return x;
}
bool judge(char up, char down) {
bool flag = true;
if (down == '2') flag = (up == '1' ? true : false);
return flag ? true : false;
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
double M[maxn][maxn];
LL E[maxn][maxn];
//打表
_rep(i, 0, 9) {
_rep(j, 1, 30) {
db m = 1 - pow(2, -1-i), e = pow(2, j) - 1;
db t = log10(m) + e * log10(2); //注意要以10为底数
E[i][j] = t; //取整,得到B
M[i][j] = pow(10, t - E[i][j]);
}
}
//输入的方法
string s;
while (cin >> s && s != "0e0") {
s[s.find('e')] = ' ';
db a; int b;
istringstream ss(s); //istringstream的使用
ss >> a >> b;
_rep(i, 0, 9) {
_rep(j, 1, 30) {
if (b == E[i][j] && fabs(a - M[i][j]) < eps) {
printf("%d %d\n", i, j);
goto OUT;
}
}
}
OUT:;
// sscanf(s, "%lf%d", &a, &b);
}
return 0;
}
贴一下用字符数组读入的方法:
//输入的方法2
char line[300];
while (scanf("%s", line) == 1 && strcmp(line, "0e0") != 0) {
*strchr(line, 'e') = ' ';
db a; int b;
sscanf(line, "%lf%d", &a, &b);
...
}
统计
巧妙处理重复问题D - Master-Mind Hints
解题思路
自己做的时候是先统计strong的字符,之后再统计出现过的字符,略显麻烦。
更好的方法:
可以直接统计个数,然后相减。
直接统计可得A,为了求B,对于每个数字(1~9),统计二者出现的次数c1和 c2,则min(c1,c2)就是该数字对B的贡献。最后要减去A的部分。代码如下:
参考代码
原来:
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define maxn 1010
int readint() {
int x; scanf("%d", &x); return x;
}
int hashTable[15];
int main() {
int n, k, cnt = 1;
vector<int> num;
while (~scanf("%d", &n) && n) {
printf("Game %d:\n", cnt++);
memset(hashTable, 0, sizeof(hashTable));
num.clear();
_for(i, 0, n) {
int t = readint();
num.pb(t);
hashTable[t]++;
}
bool flag = true;
int table[15];
while (1) {
memcpy(table, hashTable, sizeof(hashTable)); //每次都要重开一个表
bool is[maxn] = {
0};
vector<int> nums;
int scnt = 0, wcnt = 0;
_for(i, 0, n) {
scanf("%d", &k);
nums.pb(k);
if (!k) flag = false;
if (table[k] && num[i] == k) {
scnt++;
table[k]--;
is[i] = true;
}
}
if (!flag) break;
_for(i, 0, n) {
if (!is[i] && table[nums[i]]) {
table[nums[i]]--;
wcnt++;
}
}
printf(" (%d,%d)\n", scnt, wcnt);
}
}
return 0;
}
更优的方法如下:
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define maxn 1010
int readint() {
int x; scanf("%d", &x); return x;
}
int a[maxn], b[maxn];
int main() {
int n, k, cnt = 1;
while (~scanf("%d", &n) && n) {
printf("Game %d:\n", cnt++);
_for(i, 0, n) {
scanf("%d", &a[i]);
}
for(;;) {
int tmp[15];
int acnt = 0, bcnt = 0;
_for(i, 0, n) {
scanf("%d", &b[i]);
if (a[i] == b[i]) acnt++; //直接统计完全相同的个数
}
if (b[0] == 0) break;
int c1, c2;
_rep(d, 1, 9) {
c1 = 0; c2 = 0;
_for(i, 0, n) {
if (a[i] == d) c1++;
if (b[i] == d) c2++;
}
bcnt += min(c1, c2); //加上较小的那个值
}
printf(" (%d,%d)\n", acnt, bcnt - acnt);
}
}
return 0;
}
统计连续的字符个数F - Crossword Answers
解题思路
(想的有点麻烦)
读入一个表之后要进行三次遍历:
第一次进行标记,把是某个横字符串或者竖字符串的第一个字符打上标记。
第二次将标记变成标号(注意题目要求),同时把每行的横字符串都输出。
第三次把每列的竖字符串都输出。
注意事项
1.注意输出竖字符串的顺序。要按照标号的顺序进行遍历输出。
解题总结
1.找连续字符的时候用while
比较好写~
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define maxn 15
#define INF 0x3f3f3f3f
typedef long long LL;
vector<string> t;
int label[maxn][maxn];
int readint() {
int x; scanf("%d", &x); return x;
}
void l(int n, int m) {
//遍历横行
_for(i, 0, n) {
_for(j, 0, m) {
bool flag = false;
if (i == 0 && t[i][j] != '*') flag = true;
else if (j == 0 && t[i][j] != '*') flag = true;
else if (i > 0 && t[i - 1][j] == '*' && t[i][j] != '*') flag = true;
else if (j > 0 && t[i][j - 1] == '*' && t[i][j] != '*') flag = true;
if (flag) label[i][j] = 1;
}
}
printf("Across\n");
int cnt = 0, fst;
string s;
_for(i, 0, n) {
s = "";
_for(j, 0, m) {
if (label[i][j]) {
label[i][j] = ++cnt; //首先打一下标号
}
if (t[i][j] != '*') {
if (s == "") {
fst = label[i][j];
}
s += t[i][j];
}
if (t[i][j] == '*' || j == m - 1){
//这里是逐个处理的,所以没有写while循环~
if (s != "") {
printf("%3d.%s\n", fst, s.c_str());
s = "";
}
}
}
}
printf("Down\n");
s = "";
_for(i, 0, n) {
_for(j, 0, m) {
if (t[i][j] != '*' && label[i][j]) {
fst = label[i][j];
int x = i, y = j;
while (x < n && t[x][y] != '*') {
//这里比较好写
s += t[x++][y];
label[x][y] = 0; //访问过的进行标记
}
printf("%3d.%s\n", fst, s.c_str());
s = "";
}
}
}
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
string s;
int n, m, cnt = 0;
while (~scanf("%d%d", &n, &m) && n) {
getchar();
if (cnt) printf("\n");
printf("puzzle #%d:\n", ++cnt);
t.clear();
memset(label, 0, sizeof(label));
_for(i, 0, n) {
getline(cin, s);
t.pb(s);
}
l(n, m);
}
return 0;
}
统计每列最多的字母个数G - DNA Consensus String
解题思路
直接对每一列在线处理,更新当前出现次数最多的字符,最后将每列的个数减去该字符的数量,就得到与之不同的字符个数。累加得到题目要求的Hamming distances
。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define maxn 30
#define INF 0x3f3f3f3f
typedef long long LL;
vector<string> str;
int num[maxn];
int readint() {
int x; scanf("%d", &x); return x;
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int t, n, m;
scanf("%d", &t);
string s;
while (t--) {
str.clear();
scanf("%d%d\n", &n, &m);
_for(i, 0, n) {
getline(cin, s);
str.pb(s);
}
int sum = 0, less = 0;
string ans = "";
_for(j, 0, m) {
memset(num, 0, sizeof(num));
int cnt = 0;
char now = 'Z', tmp;
_for(i, 0, n) {
tmp = str[i][j];
num[tmp - 'A']++;
if (num[tmp - 'A'] > cnt || (num[tmp - 'A'] == cnt && tmp < now)) {
now = tmp;
cnt = num[tmp - 'A'];
}
}
ans = ans + now;
less += (n - num[now - 'A']);
}
printf("%s\n%d\n", ans.c_str(), less);
}
return 0;
}
统计相同的对&细节J - Box
注意事项
最多只能出现3个不同的数字。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define maxn 10010
#define INF 0x3f3f3f3f
typedef long long LL;
int hashTable[maxn];
struct p{
int a, b;
};
vector<p> v;
int readint() {
int x; scanf("%d", &x); return x;
}
void judge(int x, int &cnt) {
if (!hashTable[x]) {
hashTable[x] = 1;
cnt++;
}
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int a, b, x, y;
while (~scanf("%d%d", &a, &b)) {
v.clear();
memset(hashTable, 0, sizeof(hashTable));
int cnt = 0;
judge(a, cnt);
judge(b, cnt);
v.pb(p{
a, b});
bool yes = true; //判断是否只有小于3个数字
_for(i, 0, 5) {
scanf("%d%d", &a, &b);
judge(a, cnt);
judge(b, cnt);
bool flag = true;
if (cnt > 3) {
yes = false;
}
for (auto it = v.begin(); it != v.end(); it++) {
x = (*it).a; y = (*it).b;
if ((x == a && y == b) || (x == b && y == a)) {
v.erase(it);
flag = false;
break;
}
}
if (flag) v.pb(p{
a, b});
}
printf("%s\n", !v.size() && yes ? "POSSIBLE" : "IMPOSSIBLE");
}
return 0;
}
字符串匹配&细节K - Kickdown
注意事项
设读进来的上游是A,下游是B,可以假设B不动,去移动A,也可以假设A不动,去移动B。两种情况相当于把字符串翻转了一下,注意两种情况都要考虑。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define maxn 10010
#define INF 0x3f3f3f3f
typedef long long LL;
int hashTable[maxn];
struct p{
int a, b;
};
vector<p> v;
int readint() {
int x; scanf("%d", &x); return x;
}
bool judge(char up, char down) {
bool flag = true;
if (down == '2') flag = (up == '1' ? true : false);
return flag ? true : false;
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
string up, down;
while (getline(cin, up)) {
getline(cin, down);
//if (down.size() < up.size()) swap(up, down);
int st = 0;
bool flag = false;
int ans;
while (!flag) {
flag = true;
for (int i = st, j = 0; i < down.size() && j < up.size(); i++, j++) {
//应该是同时变化
if (!judge(up[j], down[i])) {
flag = false; goto loop; }
}
loop:
if (flag) {
//printf("%d\n", max(st+up.size(), down.size()));
ans = max(st+up.size(), down.size());
break;
}
st++;
}
swap(up, down);
flag = false;
int ans1;
st = 0;
while (!flag) {
flag = true;
for (int i = st, j = 0; i < down.size() && j < up.size(); i++, j++) {
//应该是同时变化
if (!judge(up[j], down[i])) {
flag = false; break;}
}
if (flag) {
//printf("%d\n", max(st+up.size(), down.size()));
ans1 = max(st+up.size(), down.size());
break;
}
st++;
}
printf("%d\n", min(ans, ans1));
}
return 0;
}
字符串处理
分离连续数字:例题B - Molar mass
解题思路
难题在于要在字符串中读取连续的数字。我用的是字符串的拼接,并且用到了stoi
函数。
看网上题解,其实可以直接计算。
参考代码
自己写的~
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define LOCAL //提交的时候一定要记得注释掉这句话
#define maxn 100010
#define INF 0x3f3f3f3f
int readint() {
int x; scanf("%d", &x); return x;
}
int gen[maxn]; //标记数i的生成数是gen[i]
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout); //可以把结果直接打印出来看
#endif
map<char, double> mp = {
{
'C', 12.01}, {
'H', 1.008}, {
'O', 16.00}, {
'N', 14.01}};
int t;
scanf("%d", &t);
char c;
getchar();
string s, cnt = "";
while (t--) {
cin >> s;
int next;
double sum = 0;
_for(i, 0, s.size()) {
next = i + 1;
cnt = "";
while (next < s.size() && isdigit(s[next])) {
cnt += s[next];
next++;
}
sum += mp[s[i]] * (cnt == "" ? 1 : (stoi(cnt)));
i = next - 1;
}
printf("%.3f\n", sum);
}
return 0;
}
也记录一下不用字符串的写法:
//不用字符串的写法
int q = 0;
while (isdigit(s[next])) {
q *= 10;
q += s[next] - '0';
next++;
}