paiza×Qiita記事投稿キャンペーンということで、paizaラーニングレベルアップ問題集の文字列収集をやってみました。
入力の後半でクエリ文字列が来てから市場に出回っている$N$個の文字列を精査していては、時間がかかりすぎてしまいます。予め、このクエリ文字列が来たら必要金額はいくら、ということを計算しておきましょう。クエリ文字列と必要金額は、連想配列に格納しておきます。(下の表の、太字の入力に対して、細字の文字列と数値を格納。同じ文字列(キー)が来たら数値を足し合わせる)
bcac | 3 |
---|---|
b | 3 |
bc | 3 |
bca | 3 |
bcac | 3 |
#include <iostream>
#include <map>
#include <utility>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
map<string, int> dict;
for (int i = 0; i < n; i++) {
string s;
int p;
cin >> s >> p;
for (int j = 1; j <= s.size(); j++) {
dict[s.substr(0, j)] += p; //s.substr(0,j)がdictに登録されていない場合はdict[s.substr(0,j)]に0+pが格納される。
}
}
for (int i = 0; i < m; i++) {
string q;
cin >> q;
cout << dict[q] << endl;
}
return 0;
}
C++のmap
は、連想配列に登録されていないキーに対する値を求めようとしたとき、デフォルト値が返されます(値がint
型の場合は0
)。Pythonのdefaultdict
も同様です。しかし、多くの言語では、連想配列に登録されていないキーに対する値を求めようとするとエラーになりますので、既にキーが登録されている/されていないでチェック&分岐処理が必要になります。
以上で終わりなのですが、トライ木というものを使って実装してみましょう。よく分からないという方は、リンク先やタグ遷移先を参照してください。
トライ木メニューを参考にしてトライ木を実装してみます。今回は、この問題を解けるように改変しています。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node {
int value; //合計必要金額
struct Node *child[26];
} Node;
typedef struct Trie {
Node *root;
} Trie;
void insert(Trie *trie, const char *key, int value) {
Node *node = trie->root;
for (const char *c = key; *c; c++) {
int ch = (int) (*c - 'a');
if (!node->child[ch]) {
if (!(node->child[ch] = (Node*) malloc(sizeof(Node)))) {
abort();
}
memset(node->child[ch], 0, sizeof(Node));
}
node->child[ch]->value += value;
node = node->child[ch];
}
}
int search(const Trie *trie, const char *key) {
Node *node = trie->root;
for (const char *c = key; *c; c++) {
int ch = (int) (*c - 'a');
if (!node->child[ch]) {
return 0;
}
node = node->child[ch];
}
return node->value;
}
void _clear(Node *node) {
for (char c = 'a'; c <= 'z'; c++) {
if (node->child[c - 'a']) {
_clear(node->child[c - 'a']);
}
}
free(node);
}
void clear(Trie *trie) {
_clear(trie->root);
trie->root = NULL;
}
上のトライ木を用いて、問題を解いていきます。
int main() {
int n, m;
scanf("%d %d", &n, &m);
Trie trie;
trie.root = (Node*) malloc(sizeof(Node));
memset(trie.root, 0, sizeof(Node));
for (int i = 0; i < n; i++) {
char s[101];
int p;
scanf("%s %d", s, &p);
insert(&trie, s, p);
}
for (int i = 0; i < m; i++) {
char q[101];
scanf("%s", q);
printf("%d\n", search(&trie, q));
}
clear(&trie);
return 0;
}
問題文の入力例1では、まず、以下のようにトライ木に格納されます。
bcac 3
abcd 14
abccjg 92
bcaddgie 2
abcd 6
cd 200
そして、以下のように値を取り出していきます。丸ノードの値が答えです。
b
a
abcd
gagioheo
gagioheo
で始まる単語は格納されていませんので、0
が答えです。
cb