0

我对这里已经提出的问题有一个扩展

但是,我想返回原始字符串中最长的重复字符集的列表,而不是按最高排序的 char 及其相对计数的列表。

我相当精通链接,但从未遇到过在字符串中查询 char 类型的实例,并认为有人可以给我一个提示来帮助我理解 LINQ 的特定用例......

谢谢

4

3 回答 3

4

使用链接的示例:

var largest = input.GroupBy(x => x).OrderByDescending(x => x.Count()).First();
var asString = new string(largest.Key, largest.Count());
于 2013-05-10T22:28:41.213 回答
4

我假设您想要最长的子字符串。例如,对于aabccc您想要

我还假设问题域是 Unicode 字符的字符串。不幸的是,.NETSystem.String是一系列代码单元。要计算或索引 Unicode 字符,您必须将它们作为代码点处理。最简单的方法是将编码更改为 UTF-32,因为int每个代码点都有一个,并且代码点是 Unicode 字符的数字标识符 [一般来说]。

之后,要找到相同字符的最长子序列,您必须遍历整个序列。游程编码是一种通用方法,我将其用作中间步骤。在找到最长子序列的代码点和长度后,我重新创建了它们的字符串。

        const string test = "aabccc"; // contains barber pole characters
        Console.WriteLine(test);

        var longest = test.ToCodepoints().RunLengthEncode().OrderByDescending(itemCount => itemCount.Item2).First();
        var subsequence = String.Concat(Enumerable.Repeat(Char.ConvertFromUtf32(longest.Item1), longest.Item2));
        Console.WriteLine(subsequence);

将字符串转换为代码点等同于转换为 UTF-32。它可以通过一种System.Text.Encoding方法来完成,但最终会得到一个字节数组,然后必须将其转换为代码点。这是一个 IEnumerable,它产生一个int.

    public static IEnumerable<int> ToCodepoints(this String s)
    {
        var codeunits = s.ToCharArray();
        var i = 0;

        while (i < codeunits.Length)
        {
            int codepoint;
            if (Char.IsSurrogate(codeunits[i]))
            {
                codepoint = Char.ConvertToUtf32(codeunits[i], codeunits[i + 1]);
                i += 2;
            }
            else
            {
                codepoint = codeunits[i];
                i += 1;
            }
            yield return codepoint;
        }

    }

运行长度编码为相同代码点的每个子序列生成代码点 ( Item1) 的元组和运行长度 ( ):Item2

    public static IEnumerable<Tuple<T, int>> RunLengthEncode<T>(this IEnumerable<T> sequence)
    {
        T item = default(T); // value never used
        int length = 0;
        foreach (var nextItem in sequence)
        {
            if (length == 0) // first item
            {
                item = nextItem;
                length = 1;
            }
            else if (item.Equals(nextItem)) // continuing run
            {
                length++;
            }
            else // run boundary
            {
                var run = Tuple.Create(item, length);
                item = nextItem;
                length = 1;
                yield return run;
            }
        }
        if (length > 0) // last run
        {
            yield return Tuple.Create(item, length);
        }
于 2013-05-11T04:46:36.660 回答
2

无需创建大量中间对象。您只需要跟踪最长序列中的字符和该序列的长度:

char longest = '\0';
int longestLength = 0;

char last = '\0';
int lastLength = 0;

foreach (char c in input)
{
    if (c == last)
    {
        lastLength++;

        if (lastLength > longestLength)
        {
            longestLength = lastLength;
            longest = c;
        }
    }
    else
    {
        lastLength = 1;
    }

    last = c;
}

var result = new string(longest, longestLength);
于 2013-05-12T22:39:18.823 回答