(追記)もっと簡潔になるように解き直しました。→ http://qiita.com/items/6a0774443413c71c09d9
問題は http://d.hatena.ne.jp/matarillo/20120515/p1
私の答えは、範囲内の解をすべてリストアップしています。
解が1つでよい場合は、offsetsと掛け合わせずに、shortestsの先頭の配列だけ返せばよいです。
そうすれば、メソッドの引数からstartとendを削除できます。
Program.cs
using System;
using System.Linq;
using E = System.Linq.Enumerable;
class Program
{
static void Main()
{
var result = InverseFizzBuzz(new[] { "fizz", "fizz", "buzz" }, 1, 100);
foreach (var a in result)
{
Console.WriteLine(string.Join(",", a));
}
}
static Tuple<int,string>[] table = E.Zip(
new[] { 0, 3, 5, 6, 9, 10, 12},
new[] { "fizzbuzz", "fizz", "buzz", "fizz", "fizz", "buzz", "fizz" },
(x, y) => Tuple.Create(x, y)).ToArray();
static int FromIndex(int index)
{
return (index / 7 * 15) + table[index % 7].Item1;
}
static bool IsMatch(int index, string value)
{
return table[index % 7].Item2 == value;
}
static int[][] InverseFizzBuzz(string[] input, int start, int end)
{
var list = from index in E.Range(0, 7)
let indices = E.Range(index, input.Length)
let tuples = E.Zip(indices, input, (x, y) => Tuple.Create(x, y))
where tuples.All(x => IsMatch(x.Item1, x.Item2))
let rStart = FromIndex(tuples.First().Item1)
let rEnd = FromIndex(tuples.Last().Item1)
select E.Range(rStart, rEnd - rStart + 1).ToArray();
var lookup = list
.Select(x => new { Value = x, Length = x.Length })
.ToLookup(x => x.Length)
.OrderBy(x => x.Key);
var shortests = (lookup.Count() > 0)
? lookup.First().Select(x => x.Value)
: E.Empty<int[]>();
var oStart = start / 15;
var oEnd = end / 15;
var offsets = E.Range(oStart, oEnd - oStart + 1).Select(x => x * 15);
var withOffsets = from x in shortests
from y in offsets
where (start <= x.First() + y) && (x.Last() + y <= end)
select x.Select(z => y + z).ToArray();
return withOffsets.ToArray();
}
}