用户可以从列表中选择任意周天数。算法应找到所选天数的最长连续组。如果小组持续两周,则开始日可以在结束日之后。如果它更简单,只需要检测一组至少3天的时间。由于跨越了周边界,这使得最多只能有一组人。(一周内不能有两组3天未连接。)
例如,如果用户从列表中选择星期一、星期二、星期三和星期六,则显示内容应类似于"星期一、周三和星期六"。
另一个例子是:周三,周五,周六,周日,周一->"周三,周五-周一"。
有没有一个有效的算法,最好是在C#或类似的语言中?我的C#黑客工作现在已经超过一页了(包括一些评论),仍然没有完成。
使用这个答案,略有更改:
使用接受minCount
作为参数的dtb的GroupAdjacentBy
的修改版本:
public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(
this IEnumerable<T> source, Func<T, T, bool> predicate, int minCount)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
var list = new List<T> { e.Current };
var pred = e.Current;
while (e.MoveNext())
{
// if adjacent, add to list
if (predicate(pred, e.Current))
{
list.Add(e.Current);
}
else
{
// otherwise return previous elements:
// if less than minCount elements,
// return each element separately
if (list.Count < minCount)
{
foreach (var i in list)
yield return new List<T> { i };
}
else
{
// otherwise return entire group
yield return list;
}
// create next group
list = new List<T> { e.Current };
}
pred = e.Current;
}
yield return list;
}
}
}
并将GroupAdjacentBy
的标准更改为每周分组转换:
// week starts with Monday, so this should
// represent: Wed, Fri, Sat, Sun, Mon
int[] array = new int[] { 1, 2, 4, 5, 6, 0 };
Func<int, int, bool> adjacentCriteria = (x, y) => (x+1==y) || (x==6 && y==0);
string result = string.Join(", ", array
.GroupAdjacentBy(adjacentCriteria, 3)
.Select(g => new int[] { g.First(), g.Last() }.Distinct())
.Select(g => string.Join("-", g)));
Console.WriteLine(result); // output: 1, 2, 4-0
我已经完成了我的版本。它比另一个版本长一点,但它也处理文本表示,并完成了这项任务。怎么样?
using System;
using System.Text;
namespace WeekMathTest
{
class Program
{
static void Main(string[] args)
{
string[] weekDayNames = new string[] {
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun"
};
WeekDays weekDays = WeekDays.Monday | WeekDays.Tuesday | WeekDays.Thursday | WeekDays.Saturday | WeekDays.Sunday;
Console.WriteLine(WeekDayGroup(weekDays, weekDayNames));
}
static string WeekDayGroup(WeekDays weekDays, string[] weekDayNames)
{
int groupStart = 0, groupEnd = 0, groupLength = 0;
int maxGroupStart = 0, maxGroupEnd = 0, maxGroupLength = 0;
// Iterate all days in a repeated range
// (Sat/Sun doesn't need to be repeated or it would be in the first group)
for (int day = 1; day <= 7 + 5; day++)
{
// Is this day set?
int bitValue = 1 << ((day - 1) % 7);
bool daySet = ((int) weekDays & bitValue) != 0;
if (daySet)
{
if (groupStart == 0)
{
// First day set, remember it as group start
groupStart = day;
groupEnd = day;
groupLength = 1;
}
else
{
// Group has already been started, set new end
groupEnd = day;
groupLength = groupEnd - groupStart + 1;
if (groupLength == 7)
{
// Seen every day of the week, stop here
break;
}
}
}
else
{
if (groupLength >= 3 && groupLength > maxGroupLength)
{
// Group was long enough and longer than the last one, save it
maxGroupStart = groupStart;
maxGroupEnd = groupEnd;
maxGroupLength = groupLength;
}
// Reset operation variables
groupStart = 0;
groupEnd = 0;
groupLength = 0;
}
}
// Final check
if (groupLength >= 3 && groupLength > maxGroupLength)
{
// Group was long enough and longer than the last one, save it
maxGroupStart = groupStart;
maxGroupEnd = groupEnd;
maxGroupLength = groupLength;
}
// Clear all group days from the original value
for (int day = maxGroupStart; day <= maxGroupEnd; day++)
{
int bitValue = 1 << ((day - 1) % 7);
weekDays = (WeekDays) ((int) weekDays & ~bitValue);
}
// Generate output string
StringBuilder sb = new StringBuilder();
for (int day = 1; day <= 7; day++)
{
int bitValue = 1 << ((day - 1) % 7);
bool daySet = ((int) weekDays & bitValue) != 0;
if (daySet)
{
if (sb.Length > 0) sb.Append(", ");
sb.Append(weekDayNames[day - 1]);
}
else if (day == maxGroupStart)
{
if (sb.Length > 0) sb.Append(", ");
sb.Append(weekDayNames[day - 1]);
sb.Append("-");
sb.Append(weekDayNames[(maxGroupEnd - 1) % 7]);
}
}
return sb.ToString();
}
[Flags]
enum WeekDays
{
Monday = 1,
Tuesday = 2,
Wednesday = 4,
Thursday = 8,
Friday = 16,
Saturday = 32,
Sunday = 64
}
}
}