yukicoder    次のyukicoderの問題へ

yukicoder 0006 使いものにならないハッシュ

■■■問題■■■

ハッシュに興味を持ったFrankは、ハッシュアルゴリズムに、このようなアルゴリズムを考えた。
自然数の各桁を足した合計、それも二桁以上になる場合は、それを繰り返す。
つまり、
hash(4)=4,
hash(17)=hash(1+7)=hash(8)=8,
hash(119)=hash(1+1+9)=hash(11)=hash(2)=2のようになる。

しかし実際使ってみるとコリジョン(計算値がかぶってしまうこと)が多く、あまり使い物にならなかった。

それでも諦めきれないFrankに対して、あなたは「落ち着け、素数を数えるんだ」と言った。
やけになったFrankは、自然数 [K,N] (K <= i <= N の範囲ということ) の中の
連続した素数列で上記のハッシュアルゴリズムを使用し、
コリジョンが起こらない(値がかぶらない)最大の範囲を考えようとしている。
言った手前、申し訳なくなったあなたは、Frankの代わりに求めてあげることにした。

範囲[K,N] (K <= i <= N の範囲)に含まれる連続した素数列で上記のハッシュアルゴリズムを使用した時に、
すべて異なる値になる最大の長さの素数列を求め、元の素数列の最初の素数を求めてください。
(複数同じ長さの素数列がある場合は、数が大きい方を選択する)

■■■入力■■■

K
N

1 <= K <= N
2 <= N <= 20万
[K,N]の範囲に素数が含まれるのが保証されるものとする。

■■■出力■■■

数値を文字列で出力してください。
最後に改行してください。


C#のソース

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static string InputPattern = "Input2";

    static List<string> GetInputList()
    {
        var WillReturn = new List<string>();

        if (InputPattern == "Input1") {
            WillReturn.Add("1");
            WillReturn.Add("11");
            //3
            //まず[1,11] の中に含まれる素数列は 2,3,5,7,11 である
            //Frankのハッシュアルゴリズムを適用すると 2,3,5,7,2 となる
            // ここでコリジョンが起こらない(値が重ならない)
            //最大の長さの素数列を取り出すと2,3,5,7と3,5,7,11 となる。
            //数が大きい方を選択するので素数列の最初は3となる。
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("10");
            WillReturn.Add("100");
            //31
            //[10,100] の素数列を考えると以下の長さが最高となる
            //31,37,41,43,47,53→4,1,5,7,2,8
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    static void Main()
    {
        List<string> InputList = GetInputList();
        int K = int.Parse(InputList[0]);
        int N = int.Parse(InputList[1]);

        Eratosthenes(N);

        //素数情報のListを作成
        List<SosuuInfoDef> SosuuInfoList = DeriveSosuuInfoList(K, N);

        //尺取法を使う
        int UB = SosuuInfoList.Count - 1;
        //開始素数の最大値[長さ]なDict
        var AnswerDict = new Dictionary<int, int>();

        int L = 0;
        for (int R = 0; R <= UB; R++) {
            int CurrHashVal = SosuuInfoList[R].HashVal;

            //再登場のハッシュ値の場合
            int wkInd = SosuuInfoList.FindIndex(L, R - L, X => X.HashVal == CurrHashVal);

            if (wkInd >= 0) {
                L = wkInd + 1;

                //下限値枝切り
                int RestMaxCnt = UB - L + 1;
                if (RestMaxCnt < AnswerDict.Keys.Max()) break;
            }

            AnswerDict[R - L + 1] = SosuuInfoList[L].SosuuVal;
        }
        var Tmp = AnswerDict.OrderByDescending(X => X.Key).ThenByDescending(X => X.Value);
        Console.WriteLine(Tmp.First().Value);
    }

    struct SosuuInfoDef
    {
        internal int SosuuVal;
        internal int HashVal;
    }

    //素数情報のListを作成
    static List<SosuuInfoDef> DeriveSosuuInfoList(int pK, int pN)
    {
        var WillReturn = new List<SosuuInfoDef>();
        foreach (int EachInt in SosuuArr) {
            if (EachInt < pK) continue;
            if (EachInt > pN) break;
            SosuuInfoDef WillAdd;
            WillAdd.SosuuVal = EachInt;
            WillAdd.HashVal = DeriveHashVal(EachInt);
            WillReturn.Add(WillAdd);
        }

        foreach (SosuuInfoDef EachSosuuInfo in WillReturn) {
            Console.WriteLine("{0}のハッシュ値={1}",
                EachSosuuInfo.SosuuVal, EachSosuuInfo.HashVal);
        }
        return WillReturn;
    }

    static int[] SosuuArr;

    //エラトステネスの篩
    static void Eratosthenes(int pJyougen)
    {
        var CheckArr = new System.Collections.BitArray(pJyougen + 1);
        for (int I = 2; I <= CheckArr.Count - 1; I++) {
            CheckArr[I] = true;
        }
        for (int I = 2; I * I <= CheckArr.Count - 1; I++) {
            if (I != 2 && I % 2 == 0) continue;

            if (CheckArr[I]) {
                for (int J = I * 2; J <= CheckArr.Count - 1; J += I) {
                    CheckArr[J] = false;
                }
            }
        }
        var SosuuList = new List<int>();
        for (int I = 2; I <= CheckArr.Count - 1; I++)
            if (CheckArr[I]) SosuuList.Add(I);

        SosuuArr = SosuuList.ToArray();
    }

    //ハッシュ値を求める
    static int DeriveHashVal(int pTarget)
    {
        int CurrHashVal = pTarget;
        while (CurrHashVal >= 10) {
            int CopiedVal = CurrHashVal;
            CurrHashVal = 0;
            while (CopiedVal > 0) {
                CurrHashVal += CopiedVal % 10;
                CopiedVal /= 10;
            }
        }
        return CurrHashVal;
    }
}


デバッグ情報付の実行結果

11のハッシュ値=2
13のハッシュ値=4
17のハッシュ値=8
19のハッシュ値=1
23のハッシュ値=5
29のハッシュ値=2
31のハッシュ値=4
37のハッシュ値=1
41のハッシュ値=5
43のハッシュ値=7
47のハッシュ値=2
53のハッシュ値=8
59のハッシュ値=5
61のハッシュ値=7
67のハッシュ値=4
71のハッシュ値=8
73のハッシュ値=1
79のハッシュ値=7
83のハッシュ値=2
89のハッシュ値=8
97のハッシュ値=7
31


解説

エラトステネスの篩で素数列挙してから、ハッシュ値を列挙して、
尺取法を使ってます。