Reputation: 2243
Let's say i have a string like
string text = "hello dear";
Then I want to determine the longest repetition of coherented characters - in this case it would be ll
. If there are more than one with the same count, take any.
I have tried to solve this with linq
char rchar = text.GroupBy(y => y).OrderByDescending(y => y.Count()).Select(x => x).First().Key;
int rcount = text.Where(x => x == rchar).Count();
string routput = new string(rchar, rcount);
But this returns ee
. Am I on the right track?
Upvotes: 3
Views: 5997
Reputation: 27357
If you prefer doing things with LINQ, you can do it fairly simply with a LINQ extension:
var text = "hello dear";
var result = string.Join("",
text
.GroupAdjacentBy((l, r) => (l == r)) /* Create groups where the current character is
Is the same as the previous character */
.OrderByDescending(g => g.Count()) //Order by the group lengths
.First() //Take the first group (which is the longest run)
);
With this extension (taken from Use LINQ to group a sequence of numbers with no gaps):
public static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(this IEnumerable<T> source, Func<T, T, bool> predicate)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
var list = new List<T> { e.Current };
var pred = e.Current;
while (e.MoveNext())
{
if (predicate(pred, e.Current))
{
list.Add(e.Current);
}
else
{
yield return list;
list = new List<T> { e.Current };
}
pred = e.Current;
}
yield return list;
}
}
}
}
Might be a bit overkill - but I've found GroupAdjacentBy
quite useful in other situations as well.
Upvotes: 4
Reputation: 11228
Another solution using LINQ:
string text = "hello dear";
string longestRun = new string(text.Select((c, index) => text.Substring(index).TakeWhile(e => e == c))
.OrderByDescending(e => e.Count())
.First().ToArray());
Console.WriteLine(longestRun); // ll
It selects a sequence of substrings starting with the same repeating character, and creates the result string with the longest of them.
Upvotes: 4
Reputation: 9191
Although a regex or custom Linq extension is fine, if you don't mind "doing it the old way" you can achieve your result with two temporary variables and a classic foreach
loop.
The logic is pretty simple, and runs in O(n). Loop through your string and compare the current character with the previous one.
If it's the same, increase your count
. If it's different, reset it to 1
.
Then, if your count
is greater than the previous recorded max count, overwrite the rchar
with your current character.
string text = "hello dear";
char rchar = text[0];
int rcount = 1;
int currentCount = 0;
char previousChar = char.MinValue;
foreach (char character in text)
{
if (character != previousChar)
{
currentCount = 1;
}
else
{
currentCount++;
}
if (currentCount >= rcount)
{
rchar = character;
rcount = currentCount;
}
previousChar = character;
}
string routput = new string(rchar, rcount);
It's indeed verbose, but gets the job done.
Upvotes: 4
Reputation: 45947
RegEx would be a option
string text = "hello dear";
string Result = string.IsNullOrEmpty(text) ? string.Empty : Regex.Matches(text, @"(.)\1*", RegexOptions.None).Cast<Match>().OrderByDescending(x => x.Length).First().Value;
Upvotes: 2