paizaラーニングレベルアップ問題集の【文字列処理メニュー】をC言語でやってみました。但し、最初の18問は、各問題最低1個は<string.h>
を封印したコードを掲載しています。
BASIC
文字列の出力
#include <stdio.h>
int main() {
puts("paiza");
return 0;
}
他の言語はコチラ。
文字列の受け取り
Perlっぽく。
#include <stdio.h>
char* chomp(char *s) {
char *p = s;
while (*p) p++;
while (p-- > s) if (*p == '\n') *p = '\0'; else break;
return s;
}
int main() {
char s[102];
puts(chomp(fgets(s, sizeof(s), stdin)));
return 0;
}
他の言語はコチラ
i文字目の出力
$S$の文字数の条件がありませんが、仮に100としています。
#include <stdio.h>
int main() {
char s[101];
scanf("%s", s);
int i;
scanf("%d", &i);
putchar(s[i-1]);
return 0;
}
文字列の条件判定
<string.h>
不使用
#include <stdio.h>
const char *str = "paiza";
int main() {
char s[101];
scanf("%s", s);
int i = 0;
while (s[i] && str[i]) {
if (s[i] != str[i]) break;
i++;
}
puts(s[i] == str[i] ? "YES" : "NO");
return 0;
}
<string.h>
使用
#include <stdio.h>
#include <string.h>
int main() {
char s[101];
scanf("%s", s);
puts(strcmp(s, "paiza") == 0 ? "YES" : "NO");
return 0;
}
他の言語はコチラ
文字列の文字数
<string.h>
不使用
#include <stdio.h>
int main() {
char s[10001];
scanf("%s", s);
int len = 0;
for (char *c = s; *c; c++) {
len++;
}
printf("%d\n", len);
return 0;
}
<string.h>
使用
#include <stdio.h>
#include <string.h>
int main() {
char s[10001];
scanf("%s", s);
printf("%d\n", (int) strlen(s));
return 0;
}
他の言語でもlen
関数やlength
メソッド等があります。
文字の検索
<string.h>
不使用
#include <stdio.h>
int main() {
char s[10001];
scanf("%s\n", s); // \nを入れないと次で改行コードを拾ってしまう
char c;
scanf("%c", &c);
for (int i = 0; s[i]; i++) {
if (s[i] == c) {
printf("%d\n", i + 1);
break;
}
}
return 0;
}
<string.h>
使用
#include <stdio.h>
#include <string.h>
int main() {
char s[10002];
fgets(s, sizeof(s), stdin);
char c = (char) getchar();
printf("%d\n", (int) (strchr(s, c) - s) + 1);
return 0;
}
文字列の連結
<string.h>
不使用
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
char t[1000001] = "";
char *p = t;
for (int i = 0; i < n; i++) {
char s[10001];
scanf("%s", s);
char *c = s;
while (*c) *p++ = *c++;
}
puts(t);
return 0;
}
<string.h>
不使用2
sprintf
関数の返却値と、アドレス演算を利用して、
他の言語と同様に+=
を使って連結してみました。
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
char t[1000001] = "";
char *p = t;
for (int i = 0; i < n; i++) {
char s[10001];
scanf("%s", s);
p += sprintf(p, "%s", s);
}
puts(t);
return 0;
}
<string.h>
使用
#include <stdio.h>
#include <string.h>
int main() {
int n;
scanf("%d", &n);
char t[1000001] = "";
for (int i = 0; i < n; i++) {
char s[10001];
scanf("%s", s);
strcat(t, s);
}
puts(t);
return 0;
}
他の言語では、String
クラスの文字列を+=
演算子で結合するよりも、stringstream
やStringBuffer
クラスを用いた方がベターでしょう。
NORMAL
部分文字列
<string.h>
不使用1
1文字ずつ出力します。
#include <stdio.h>
int main() {
char s[10001];
scanf("%s", s);
int i, j;
scanf("%d %d", &i, &j);
i--;
for (; i < j; i++) putchar(s[i]);
puts("");
return 0;
}
<string.h>
不使用2
ポインタを用います。
#include <stdio.h>
int main() {
char s[10001];
scanf("%s", s);
int i, j;
scanf("%d %d", &i, &j);
i--;
char t[j-i+1];
char *p = t;
for (; i < j; i++) *p++ = s[i];
*p = '\0';
puts(t);
return 0;
}
<string.h>
使用
#include <stdio.h>
#include <string.h>
int main() {
char s[10001];
scanf("%s", s);
int i, j;
scanf("%d %d", &i, &j);
i--;
char t[j-i+1];
strncpy(t, s+i, j-i);
t[j-i] = '\0';
puts(t);
return 0;
}
他の言語はコチラが参考になると思います。
文字列の挿入
<string.h>
不使用1
#include <stdio.h>
int main() {
char s[10001];
scanf("%s", s);
char t[10001];
scanf("%s", t);
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) putchar(s[i]);
printf("%s", t);
for (char* c = s + n; *c; c++) putchar(*c);
return 0;
}
<string.h>
不使用2
#include <stdio.h>
int main() {
char s[10001];
scanf("%s", s);
char t[10001];
scanf("%s", t);
int n;
scanf("%d", &n);
char u[20001] = "";
char *p = u;
for (int i = 0; i < n; i++) *p++ = s[i];
char *c = t;
while (*c) *p++ = *c++;
c = s + n;
while (*c) *p++ = *c++;
*p = '\0';
puts(u);
return 0;
}
<string.h>
使用
#include <stdio.h>
#include <string.h>
int main() {
char s[10001];
scanf("%s", s);
char t[10001];
scanf("%s", t);
int n;
scanf("%d", &n);
char u[20001] = "";
strncpy(u, s, n);
strcat(u, t);
strcat(u, s + n);
puts(u);
return 0;
}
文字列の書き換え
#include <stdio.h>
int main() {
char s[101];
scanf("%s", s);
int i;
char c;
scanf("%d %c", &i, &c);
s[i-1] = c;
puts(s);
return 0;
}
- Javaでは受け取った文字列を
toCharArray
する必要があります
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
char[] s = sc.next().toCharArray();
int i = sc.nextInt() - 1;
char c = sc.next().charAt(0);
s[i] = c;
System.out.println(new String(s));
sc.close();
}
}
- Pythonでは受け取った文字列を
list
に変換する必要があります
s = list(input())
i, c = input().split()
i = int(i) - 1
s[i] = c
print(''.join(s))
文字列から数値への変換
<stdlib.h>
不使用
#include <stdio.h>
int main() {
char s[9];
scanf("%s", s);
int n = 0;
for (char *c = s; *c; c++) n = 10 * n + (*c - '0');
printf("%d\n", n - 813);
return 0;
}
<stdlib.h>
使用
#include <stdio.h>
#include <stdlib.h>
int main() {
char s[9];
scanf("%s", s);
int n = atoi(s);
printf("%d\n", n - 813);
return 0;
}
数値から文字列への変換
#include <stdio.h>
int main() {
long long x;
scanf("%lld", &x);
long long y;
scanf("%lld", &y);
int n;
scanf("%d", &n);
char s[13];
sprintf(s, "%lld", x + y);
putchar(s[n-1]);
puts("");
return 0;
}
大文字から小文字への変換
<ctype.h>
不使用
#include <stdio.h>
int main() {
char s[10001];
scanf("%s", s);
for (char *c = s; *c; c++)
if ('A' <= *c && *c <= 'Z')
*c = *c - 'A' + 'a';
puts(s);
return 0;
}
<ctype.h>
使用
#include <stdio.h>
#include <ctype.h>
int main() {
char s[10001];
scanf("%s", s);
for (char *c = s; *c; c++)
if (isupper(*c))
*c = tolower(*c);
puts(s);
return 0;
}
小文字から大文字への変換
<ctype.h>
不使用
#include <stdio.h>
int main() {
char s[10001];
scanf("%s", s);
for (char *c = s; *c; c++)
if ('a' <= *c && *c <= 'z')
*c = *c - 'a' + 'A';
puts(s);
return 0;
}
<ctype.h>
使用
#include <stdio.h>
#include <ctype.h>
int main() {
char s[10001];
scanf("%s", s);
for (char *c = s; *c; c++)
if (islower(*c))
*c = toupper(*c);
puts(s);
return 0;
}
大文字小文字の反転
<ctype.h>
不使用
#include <stdio.h>
int main() {
char s[101];
scanf("%s", s);
for (char *c = s; *c; c++)
if ('A' <= *c && *c <= 'Z')
*c = *c - 'A' + 'a';
else if ('a' <= *c && *c <= 'z')
*c = *c - 'a' + 'A';
puts(s);
return 0;
}
<ctype.h>
使用
#include <stdio.h>
#include <ctype.h>
int main() {
char s[10001];
scanf("%s", s);
for (char *c = s; *c; c++)
if (isupper(*c))
*c = tolower(*c);
else if (islower(*c))
*c = toupper(*c);
puts(s);
return 0;
}
文字列の検索
<string.h>
不使用
#include <stdio.h>
char* find(const char *s, const char *t) {
for (const char *p = s;; p++) {
if (*p) {
for (int i = 0;; i++) {
if (t[i]) {
if (!p[i]) return NULL;
if (p[i] != t[i]) break;
} else {
return (char*) p;
}
}
} else {
return NULL;
}
}
}
int main() {
char s[101];
scanf("%s", s);
char t[101];
scanf("%s", t);
puts(find(s, t) ? "YES" : "NO");
return 0;
}
<string.h>
使用
#include <stdio.h>
#include <string.h>
int main() {
char s[101];
scanf("%s", s);
char t[101];
scanf("%s", t);
puts(strstr(s, t) ? "YES" : "NO");
return 0;
}
文字列の反転
#include <stdio.h>
int main() {
char s[1001];
scanf("%s", s);
char *p = s;
while (*p) p++;
char t[1001] = "";
char *q = t;
while (p > s) *q++ = *(--p);
puts(t);
return 0;
}
回文判定
#include <stdio.h>
int is_palindrome(const char* s) {
const char *p = s;
while (*p) p++;
const char *q = s;
while(q < --p) if (*q++ != *p) return 0;
return 1;
}
int main() {
char s[1001];
scanf("%s", s);
puts(is_palindrome(s) ? "YES" : "NO");
return 0;
}
前問の反転文字列を用いて比較する方法もあります。
ADVANCE
文字列の分割
分割した文字列を文字列の配列に格納することは省略します。
<string.h>
不使用1
#include <stdio.h>
int main() {
char s[101];
scanf("%s", s);
char t[101];
char *q = t;
for (char *p = s; *p; p++) {
if (*p == ',') {
*q = '\0';
puts(t);
q = t;
} else {
*q++ = *p;
}
}
*q = '\0';
puts(t);
return 0;
}
<string.h>
不使用2
#include <stdio.h>
int main() {
char s[101];
scanf("%s", s);
for (char *p = s; *p; p++) {
if (*p == ',') {
*p = '\n';
}
}
puts(s);
return 0;
}
<string.h>
使用
#include <stdio.h>
#include <string.h>
int main() {
char s[101];
scanf("%s", s);
char* t = strtok(s, ",");
while (t) {
puts(t);
t = strtok(NULL, ",");
}
return 0;
}
日時データの変換その1
解答例1
#include <stdio.h>
int main() {
int year, month, day, hour, minute;
scanf("%d/%d/%d/%d:%d", &year, &month, &day, &hour, &minute);
printf("%04d\n", year);
printf("%02d\n", month);
printf("%02d\n", day);
printf("%02d\n", hour);
printf("%02d\n", minute);
return 0;
}
解答例2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char datetime[17];
scanf("%s", datetime);
printf("%04d\n", atoi(strtok(datetime, "/")));
printf("%02d\n", atoi(strtok(NULL, "/")));
printf("%02d\n", atoi(strtok(NULL, "/")));
printf("%02d\n", atoi(strtok(NULL, ":")));
printf("%02d\n", atoi(strtok(NULL, "")));
return 0;
}
日時データの変換その2
解答例1
#include <stdio.h>
int main() {
int year, month, day, hour, minute;
scanf("%d/%d/%d %d:%d", &year, &month, &day, &hour, &minute);
printf("%04d\n", year);
printf("%02d\n", month);
printf("%02d\n", day);
printf("%02d\n", hour);
printf("%02d\n", minute);
return 0;
}
解答例2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char date[11], time[6];
scanf("%s %s", date, time);
printf("%04d\n", atoi(strtok(date, "/")));
printf("%02d\n", atoi(strtok(NULL, "/")));
printf("%02d\n", atoi(strtok(NULL, "/")));
printf("%02d\n", atoi(strtok(time, ":")));
printf("%02d\n", atoi(strtok(NULL, ":")));
return 0;
}
数値判定
<ctype.h>
不使用
#include <stdio.h>
int is_numeric(const char* s) {
for (const char *c = s; *c; c++) {
if (*c < '0' || '9' < *c) return 0;
}
return 1;
}
int main() {
char s[10001];
scanf("%s", s);
puts(is_numeric(s) ? "YES" : "NO");
return 0;
}
<ctype.h>
使用
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
bool is_numeric(const char* s) {
for (const char *c = s; *c; c++) {
if (!isdigit(*c)) return false;
}
return true;
}
int main() {
char s[10001];
scanf("%s", s);
puts(is_numeric(s) ? "YES" : "NO");
return 0;
}
重複の削除
解答例1(O(|S|^2)解法)
#include <stdio.h>
int main() {
char s[1001];
scanf("%s", s);
for (int i = 0; s[i]; i++) {
for (int j = 0; j <= i; j++) {
if (j == i) putchar(s[i]);
else if (s[j] == s[i]) break;
}
}
puts("");
return 0;
}
解答例2(O(|S|)解法)
#include <stdio.h>
int main() {
char s[1001];
scanf("%s", s);
int count[10] = {};
for (char *c = s; *c; c++) {
int i = (int) (*c - '0');
if (!count[i]++) putchar(*c);
}
puts("");
return 0;
}
パスワード作成
解答例1
#include <stdio.h>
int main() {
int N;
scanf("%d", &N);
char s[N + 1];
for (int i = 0; i <= N; i++) s[i] = '\0';
int Q;
scanf("%d", &Q);
for (int q = 0; q < Q; q++) {
int k;
char c;
scanf("%d %c\n", &k, &c);
s[k - 1] = c;
}
char C = (char) getchar();
for (int i = 0; i < N; i++) if (!s[i]) s[i] = C;
puts(s);
return 0;
}
解答例2
#include <stdio.h>
#include <string.h>
int main() {
int N;
scanf("%d", &N);
int Q;
scanf("%d", &Q);
int A[Q];
char C[Q];
for (int q = 0; q < Q; q++)
scanf("%d %c\n", &A[q], &C[q]);
char s[N + 1];
memset(s, getchar(), sizeof(s));
s[N] = '\0';
for (int q = 0; q < Q; q++)
s[A[q] - 1] = C[q];
puts(s);
return 0;
}
表記の訂正
方針
- 正しい小数点の位置を取得する。無い場合は末尾とする
- (ミス1対応)先頭の0を除去する。但し、小数点の直前の0は残す
- (ミス3対応)正しくない小数点を除去する
- (ミス2対応)末尾の不要な0を除去する
- 末尾が小数点になった場合、除去する
- 今回は「答えが
.0
の形式になるような値は与えられません」ので不要ですが、一応実装します
- 今回は「答えが
解答例1
#include <stdio.h>
int main() {
char S[10001];
scanf("%s", S);
int n = 0;
int p = -1;
while (S[n]) {
if (S[n] == '.' && p == -1) p = n;
n++;
}
if (p == -1) p = n;
// miss 1
int a = 0;
while (S[a] == '0' && a < p - 1) {
a++;
}
// miss 3
char T[10001] = "";
int m = 0;
int q = -1;
for (int i = a; i < n; i++) {
if (S[i] == '.') {
if (i == p) q = m;
else continue;
}
T[m++] = S[i];
}
if (q == -1) q = m;
// miss 2
while (m > q && T[m-1] == '0') {
T[--m] = '\0';
}
if (T[m-1] == '.') T[--m] = '\0';
puts(T);
return 0;
}
解答例2
#include <stdio.h>
#include <string.h>
int main() {
char S[10001];
scanf("%s", S);
char *p = strchr(S, '.');
// miss 1
char *s = S;
while ((int) (p - s) > 1 && *s == '0') s++;
char T[10001] = "";
strcpy(T, strtok(s, "."));
if (p) {
char *q = T + strlen(T);
*q = '.';
// miss 3
char *t = strtok(NULL, ".");
while (t) {
strcat(T, t);
t = strtok(NULL, ".");
}
// miss 2
char *z = T + strlen(T);
while (z > q && *(z-1) == '0') *(--z) = '\0';
if (*(z-1) == '.') *(--z) = '\0';
}
puts(T);
return 0;
}
数式の計算(1桁)
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main() {
char s[10001];
scanf("%s", s);
int ans = 0;
int tmp = 0;
char op = '+';
for (char *c = s; *c; c++) {
if (strchr("+-", *c)) {
if (op == '+') ans += tmp;
else if (op == '-') ans -= tmp;
op = *c;
} else if (isdigit(*c)) {
tmp = (int) (*c - '0');
}
}
if (op == '+') ans += tmp;
else if (op == '-') ans -= tmp;
printf("%d\n", ans);
return 0;
}
数式の計算
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main() {
char s[10001];
scanf("%s", s);
int ans = 0;
int tmp = 0;
char op = '+';
for (char *c = s; *c; c++) {
if (strchr("+-", *c)) {
if (op == '+') ans += tmp;
else if (op == '-') ans -= tmp;
tmp = 0;
op = *c;
} else if (isdigit(*c)) {
int d = (int) (*c - '0');
tmp = 10 * tmp + d;
}
}
if (op == '+') ans += tmp;
else if (op == '-') ans -= tmp;
printf("%d\n", ans);
return 0;
}
巨大な数の足し算(繰り上がりなし)
今回は
- 全ての桁において繰り上がりが発生しない
- オペランドの桁数が等しい
ので、上位桁から計算します。
#include <stdio.h>
int main() {
char s[1001];
scanf("%s", s);
char t[1001];
scanf("%s", t);
char u[1001] = "";
for (int i = 0; s[i]; i++) {
// u[i] = (s[i] - '0') + (t[i] - '0') + '0';
u[i] = s[i] + t[i] - '0'; // s[i]+t[i]でオーバーフローは発生しない
}
printf("%s\n", u);
return 0;
}
巨大な数の足し算
#include <stdio.h>
#include <string.h>
int main() {
char s[1001];
scanf("%s", s);
char t[1001];
scanf("%s", t);
char u[1002] = "";
int n = (int) strlen(s);
int c = 0; // carrying(繰り上がり)
for (int i = n; i > 0; i--) {
int d = c + (s[i-1] - '0') + (t[i-1] - '0');
u[i] = d % 10 + '0';
c = d / 10;
}
u[0] = c + '0';
char *p = u;
while (*p && *p == '0') p++;
puts(p);
return 0;
}
別解(オペランドの桁数が異なっても対応できるように)
#include <stdio.h>
#include <string.h>
int max(int a, int b) {
return a > b ? a : b;
}
int main() {
char s[1001];
scanf("%s", s);
int n = (int) strlen(s);
char t[1001];
scanf("%s", t);
int m = (int) strlen(t);
int k = max(n, m);
char u[1002] = "";
int c = 0; // carrying(繰り上がり)
for (int i = 0; i < k; i++) {
int d = c;
if (i < n) d += (s[n - i - 1] - '0');
if (i < m) d += (t[m - i - 1] - '0');
u[k - i] = d % 10 + '0';
c = d / 10;
}
u[0] = c + '0';
char *p = u;
while (*p && *p == '0') p++;
puts(p);
return 0;
}
巨大な数のかけ算
#include <stdio.h>
#include <string.h>
int main() {
char s[1001];
scanf("%s", s);
int n = (int) strlen(s);
int t;
scanf("%d", &t);
char u[1002] = "";
int c = 0; // carrying(繰り上がり)
for (int i = n; i > 0; i--) {
int d = c + (s[i-1] - '0') * t;
u[i] = d % 10 + '0';
c = d / 10;
}
u[0] = c + '0';
char *p = u;
while (*p && *p == '0') p++;
puts(p);
return 0;
}
別解(乗数が2桁以上でも対応できるように)
#include <stdio.h>
#include <string.h>
int main() {
char s[1001];
scanf("%s", s);
int n = (int) strlen(s);
char t[1001];
scanf("%s", t);
int m = (int) strlen(t);
char u[2001] = "";
memset(u, '0', n + m);
for (int j = m; j > 0; j--) {
int c = 0; // carrying(繰り上がり)
for (int i = n; i > 0; i--) {
int d = (u[i+j-1] - '0') + c + (s[i-1] - '0') * (t[j-1] - '0');
u[i+j-1] = d % 10 + '0';
c = d / 10;
}
int k = 1;
while (c) {
int d = (u[j-k] - '0') + c;
u[j-k] = d % 10 + '0';
c = d / 10;
k++;
}
}
char *p = u;
while (*p && *p == '0') p++;
puts(p);
return 0;
}
最後の2問は、言語によっては、入力値をreverse
して計算した結果をreverse
するのがいいかも。