{ }codelper
文字列の分割辞書の検索P0236

アドレスの後ろの設定を読む

ウェブアドレスの疑問符の後ろに付く設定文字列が一行与えられる。
設定文字列は英小文字、数字、'='、'&' だけでできている。
まず設定文字列を '&' で分ける。
分けた断片のうち空の断片は捨てる。
空の断片は & が続いたり、先頭や末尾に & があるときにできる。
残った断片ごとに、最初に出てくる '=' の前が名前で、後ろが値である。
値には '=' がさらに含まれることがあり、値が空文字列のこともある。
'=' のない断片は断片全体が名前で、値は空文字列である。
どの断片も '=' で始まらない。
二行目に質問の数 q、続く q 行に名前が一つずつ与えられる。
一行目に、残った断片の個数を出力せよ。
続いて質問ごとに一行に、その名前を持つ断片の値を出てきた順に一つずつ角かっこで囲み、空白なしでつなげて出力せよ。
値が空文字列なら [] と出力する。
その名前を持つ断片がなければ - を出力する。

入力
設定文字列は 1〜10,000 文字、1 ≤ q ≤ 100、質問する名前は英小文字と数字からなる 1〜10 文字
出力
一行目に断片の個数、続く q 行に質問ごとの値または -
例 1 · stdin
page=2&sort=new&&tag=a&tag=b=c&debug&
4
tag
debug
page
size
stdout
5
[a][b=c]
[]
[2]
-
例 2 · stdin
&&&
1
a
stdout
0
-
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

分割の関数は 8 言語すべてにありますが、空の断片と「分ける回数」の扱いがそれぞれ違います。
Python split("&")、JS・TS split("&")、Craykon 分ける("&") は空の断片をそのまま残すので、"&&&" は空文字列 4 個になり、自分で取り除く必要があります。
Java split("&") は正規表現で分け、末尾の空の断片だけを捨てて、先頭と途中の空の断片は残します。
C# Split('&', StringSplitOptions.RemoveEmptyEntries) は空の断片をすべて捨てます。
C strtok は続いた & を一つとみなし空の断片を飛ばすので、'&' のほうにはぴったりです。
しかし '=' まで strtok で切ると "k==" の値 "=" が消えるので、strchr で最初の '=' だけを探します。
C++ の std::string には分割の関数がないので、getline(stream, piece, '&') で一つずつ取り出し find('=') で切るのが一般的です。
この問題の落とし穴は、'=' を最初の一回だけ分けることです。
Python partition("=") や split("=", 1)、Java split("=", 2)、C# Split('=', 2) は最初の '=' でだけ切り、残りを値に残します。
JS・TS の split("=", 2) は名前が同じでも意味が違います。
前の二つの断片だけを残して残りを捨てるので "b=c=d" が ["b", "c"] になります。
indexOf("=") と slice で切りましょう。
Craykon も 位置検索("=") と 切り出す で切ります。
Java split("=") のように上限なしで切ると "k==" が ["k"] になり、値がまるごとなくなります。
同じ名前が二回出たら値をすべて見せる必要があるので、辞書には値一つではなく値のリストを入れます。

Pythonmain.py · 18 行
line = input()q = int(input())values = {}  # 이름 → 값 목록(나온 차례대로)count = 0for piece in line.split("&"):    if piece == "":        continue  # &&·맨 앞·맨 뒤의 &가 만든 빈 조각은 버린다    count += 1    # 처음 나오는 '='에서만 한 번 나눈다(값 안의 '='는 그대로)    name, _, value = piece.partition("=")    values.setdefault(name, []).append(value)print(count)for _ in range(q):    name = input().strip()    if name in values:        print("".join(f"[{v}]" for v in values[name]))    else:        print("-")
JavaScriptmain.js · 20 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[1]);const values = new Map(); // 이름 → 값 배열(나온 차례대로)let count = 0;for (const piece of lines[0].trim().split("&")) {  if (piece === "") continue; // 빈 조각은 버린다  count++;  // 처음 나오는 '='에서만 자른다. split("=")은 값 안의 '='까지 자른다  const at = piece.indexOf("=");  const name = at < 0 ? piece : piece.slice(0, at);  const value = at < 0 ? "" : piece.slice(at + 1);  if (!values.has(name)) values.set(name, []);  values.get(name).push(value);}const out = [String(count)];for (let i = 0; i < q; i++) {  const vs = values.get(lines[2 + i].trim());  out.push(vs ? vs.map((v) => `[${v}]`).join("") : "-");}console.log(out.join("\n"));
TypeScriptmain.ts · 22 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const q: number = Number(lines[1]);const values = new Map<string, string[]>(); // 이름 → 값 배열(나온 차례대로)let count = 0;for (const piece of lines[0].trim().split("&")) {  if (piece === "") continue; // 빈 조각은 버린다  count++;  // 처음 나오는 '='에서만 자른다. split("=")은 값 안의 '='까지 자른다  const at: number = piece.indexOf("=");  const name: string = at < 0 ? piece : piece.slice(0, at);  const value: string = at < 0 ? "" : piece.slice(at + 1);  const list: string[] = values.get(name) ?? [];  list.push(value);  values.set(name, list);}const out: string[] = [String(count)];for (let i = 0; i < q; i++) {  const vs: string[] | undefined = values.get(lines[2 + i].trim());  out.push(vs ? vs.map((v) => `[${v}]`).join("") : "-");}console.log(out.join("\n"));
JavaMain.java · 37 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String line = in.readLine().trim();        int q = Integer.parseInt(in.readLine().trim());        Map<String, List<String>> values = new HashMap<>(); // 이름 → 값 목록(나온 차례대로)        int count = 0;        // Java의 split은 맨 뒤의 빈 조각을 버리지만 맨 앞·가운데의 빈 조각은 남긴다        for (String piece : line.split("&")) {            if (piece.isEmpty()) continue;            count++;            // 한도 2: 처음 '='에서 한 번만 자르고, 뒤의 빈 문자열도 남긴다            String[] kv = piece.split("=", 2);            String value = kv.length > 1 ? kv[1] : "";            values.computeIfAbsent(kv[0], k -> new ArrayList<>()).add(value);        }        StringBuilder out = new StringBuilder();        out.append(count).append('\n');        for (int i = 0; i < q; i++) {            List<String> vs = values.get(in.readLine().trim());            if (vs == null) {                out.append("-\n");                continue;            }            for (String v : vs) out.append('[').append(v).append(']');            out.append('\n');        }        System.out.print(out);    }}
C#Main.cs · 43 行
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        string line = Console.ReadLine().Trim();        int q = int.Parse(Console.ReadLine());        var values = new Dictionary<string, List<string>>(); // 이름 → 값 목록(나온 차례대로)        int count = 0;        // RemoveEmptyEntries가 빈 조각을 버린다        foreach (string piece in line.Split('&', StringSplitOptions.RemoveEmptyEntries))        {            count++;            // 한도 2: 처음 '='에서 한 번만 자른다            string[] kv = piece.Split('=', 2);            string value = kv.Length > 1 ? kv[1] : "";            if (!values.TryGetValue(kv[0], out var list))            {                list = new List<string>();                values[kv[0]] = list;            }            list.Add(value);        }        var sb = new StringBuilder();        sb.Append(count).Append('\n');        for (int i = 0; i < q; i++)        {            if (values.TryGetValue(Console.ReadLine().Trim(), out var vs))            {                foreach (string v in vs) sb.Append('[').Append(v).Append(']');                sb.Append('\n');            }            else            {                sb.Append("-\n");            }        }        Console.Write(sb);    }}
C++main.cpp · 36 行
#include <iostream>#include <map>#include <sstream>#include <string>#include <vector> int main() {    std::string line, piece;    std::getline(std::cin, line);    if (!line.empty() && line.back() == '\r') line.pop_back();    std::map<std::string, std::vector<std::string>> values; // 이름 → 값 목록(나온 차례대로)    int count = 0;    std::stringstream ss(line);    // getline에 구분 글자를 주면 빈 조각도 하나씩 돌려준다    while (std::getline(ss, piece, '&')) {        if (piece.empty()) continue;        count++;        std::size_t at = piece.find('='); // 처음 '='만        if (at == std::string::npos) values[piece].push_back("");        else values[piece.substr(0, at)].push_back(piece.substr(at + 1));    }    std::cout << count << '\n';    int q;    std::cin >> q;    std::string key;    for (int i = 0; i < q; i++) {        std::cin >> key;        auto it = values.find(key);        if (it == values.end()) {            std::cout << "-\n";            continue;        }        for (const auto &v : it->second) std::cout << '[' << v << ']';        std::cout << '\n';    }}
Cmain.c · 39 行
#include <stdio.h>#include <string.h> static char line[10005];static char *names[5001], *vals[5001]; int main(void) {    int q, count = 0;    if (!fgets(line, sizeof line, stdin)) return 0;    line[strcspn(line, "\r\n")] = '\0';    /* strtok은 이어진 &를 하나로 보고 빈 조각을 건너뛴다 — 이 문제에는 딱 맞다 */    for (char *piece = strtok(line, "&"); piece; piece = strtok(NULL, "&")) {        /* '='는 strtok으로 자르면 "a==b"의 '='가 사라진다. 처음 '='만 strchr로 찾는다 */        char *eq = strchr(piece, '=');        if (eq) {            *eq = '\0';            vals[count] = eq + 1;        } else {            vals[count] = "";        }        names[count] = piece;        count++;    }    printf("%d\n", count);    scanf("%d", &q);    char key[16];    for (int i = 0; i < q; i++) {        scanf("%15s", key);        int found = 0;        for (int j = 0; j < count; j++) {            if (strcmp(names[j], key) == 0) {                printf("[%s]", vals[j]);                found = 1;            }        }        puts(found ? "" : "-");    }    return 0;}
Craykonmain.cray · 44 行
line = コンソール.行読み().空白除去();q = コンソール.行読み().数に();values = 辞書(); // 이름 → 값 목록(나온 차례대로)count = 0;それぞれ (piece の中 line.分ける("&")){    もし (piece == "")    {        次へ; // 빈 조각은 버린다    }    count += 1;    // 처음 나오는 '='에서만 자른다    at = piece.位置検索("=");    name = piece;    value = "";    もし (at >= 0)    {        name = piece.切り出す(0, at);        value = piece.切り出す(at + 1);    }    もし (!values.含む(name))    {        values[name] = [];    }    values[name].追加(value);}コンソール.書く(count);繰り返す (i = 0; i < q; i++){    key = コンソール.行読み().空白除去();    もし (values.含む(key))    {        out = "";        それぞれ (v の中 values[key])        {            out += "[" + v + "]";        }        コンソール.書く(out);    }    でなければ    {        コンソール.書く("-");    }}