{ }codelper
ポインタP0074

ポインタで歩くいちばん長いかたまり

一行目に n、二行目に n 個の整数が与えられる。
同じ数が続けて並んでいる部分をかたまりと呼ぶ(数一つも長さ 1 のかたまりである)。
いちばん長いかたまりの開始位置(1 から数える)と長さを空白一つで区切って出力せよ。
いちばん長いかたまりが複数あれば、いちばん前のものを出力する。
配列の先頭要素と末尾のすぐ次を指すポインタ二つを受け取り、ポインタを一要素ずつ進めて歩き、いちばん長いかたまりの先頭を指すポインタを返す関数を作って解いてみよう。
開始位置はそのポインタから配列の先頭のポインタを引いて求める。

入力
1 ≤ n ≤ 100,000、すべての数は -1,000,000,000 以上 1,000,000,000 以下の整数
出力
一行:開始位置 長さ
例 1 · stdin
7
3 1 2 2 2 1 1
stdout
3 3
例 2 · stdin
5
1 2 2 3 3
stdout
2 2
main.py
入力(stdin)

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

$ 準備完了

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

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

この問題は C・C++ だけを受け付けます。
アドレスを持って p++ で進むポインタとポインタの引き算はこの二つにしかなく、Python・JavaScript・TypeScript・Java・Craykon は添字 i で歩きます(C# は unsafe の中だけポインタを使えます)。
関数は const int *longest_run(const int *first, const int *last, int *len) のように [first, last) を受けます。
last = a + n は末尾のすぐ次なので指すだけで読まず、ループの条件は p != last です。
同じ配列の中の二つのポインタを引くとバイト数ではなく要素数が出るので、start - a が 0 から数えた位置、q - p がかたまりの長さです。
C++ は a.data() と a.data() + a.size() を渡し、実際には同じ形のイテレータ(a.begin()、a.end())をよく使います。
落とし穴三つ:① 関数の中で sizeof(a) / sizeof(a[0]) で長さを求めると、引数 int a[] は実はポインタなので 64 ビットでは 8 / 4 = 2 になり、先頭の二要素しか見ません。
だから長さか末尾ポインタを別に渡します。
② (start - a) / sizeof(int) のようにさらに割ってはいけません。
ポインタの引き算はすでに要素数です。
③ 長さを >= で比べると、同じ長さのとき後ろのかたまりに変わります。
長いとき(>)だけ変えます。

C++main.cpp · 31 行
#include <iostream>#include <vector> // [first, last) 를 포인터로 걷는다. 길이는 참조로 돌려준다const int* longest_run(const int* first, const int* last, int& len) {    const int* best = first;    len = 0;    for (const int* p = first; p != last;) {        const int* q = p;        while (q != last && *q == *p) ++q;        if (q - p > len) {            best = p;            len = static_cast<int>(q - p);        }        p = q;    }    return best;} int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n;    std::cin >> n;    std::vector<int> a(n);    for (int& v : a) std::cin >> v;     int len;    const int* start = longest_run(a.data(), a.data() + a.size(), len);    std::cout << (start - a.data()) + 1 << " " << len << "\n";}
Cmain.c · 34 行
#include <stdio.h>#include <stdlib.h> /* [first, last) 구간을 포인터로 걸으며 가장 긴 같은 수 덩이를 찾는다.   덩이의 시작을 가리키는 포인터를 돌려주고, 길이는 *len에 써 넣는다. */const int *longest_run(const int *first, const int *last, int *len) {    const int *best = first;    *len = 0;    const int *p = first;    while (p != last) {        const int *q = p;        while (q != last && *q == *p) q++;  /* q는 덩이 바로 다음 칸 */        if (q - p > *len) {                 /* 포인터 빼기 = 칸 수. 더 길 때만 바꿔서 비기면 앞의 것 */            best = p;            *len = (int)(q - p);        }        p = q;    }    return best;} int main(void) {    int n;    scanf("%d", &n);    int *a = malloc(n * sizeof *a);    for (int i = 0; i < n; i++) scanf("%d", &a[i]);     int len;    const int *start = longest_run(a, a + n, &len);  /* a + n은 끝 바로 다음: 가리키기만 하고 읽지 않는다 */    printf("%d %d\n", (int)(start - a) + 1, len);    /* start - a 가 0부터 센 자리 */     free(a);    return 0;}