测试字符串中重复的字符

时间:2009-05-06 13:24:53

标签: c# algorithm string

我正在用字符串做一些工作,我有一个场景,我需要确定一个字符串(通常是一个小的< 10个字符)是否包含重复的字符。

`ABCDE`  // does not contain repeats 
`AABCD`  // does contain repeats, ie A is repeated

我可以遍历string.ToCharArray()并测试char []中每个其他角色的每个角色,但我觉得我错过了一些明显的东西....也许我只需要咖啡。有人可以帮忙吗?

修改

字符串将被排序,因此顺序并不重要,因此ABCDA => AABCD

重复的频率也很重要,所以我需要知道重复是双线还是三线等。

11 个答案:

答案 0 :(得分:16)

如果字符串已排序,您可以依次记住每个字符并检查以确保下一个字符永远不会与最后一个字符相同。

除此之外,对于十个字符以下的字符串,仅仅针对所有其他字符测试每个字符可能与大多数其他事物一样快或更快。正如另一位评论者所建议的那样,有点向量可能更快(如果您有一小组合法字符,则会有所帮助。)

Bonus:这是一个实现Jon功能的灵活的LINQ解决方案:

int longestRun =
    s.Select((c, i) => s.Substring(i).TakeWhile(x => x == c).Count()).Max();

所以,好吧,它不是很快!你有问题吗?!

: - )

答案 1 :(得分:9)

如果字符串很短,那么循环和测试可能是最简单和最有效的方法。我的意思是你可以创建一个哈希集(在你正在使用的任何平台上)并迭代字符,如果字符已经在集合中并且将其添加到集合中则失败 - 但这只是当字符串更长时,可能会提供任何好处。

编辑:现在我们知道它已经排序,mquander's answer是最好的IMO。这是一个实现:

public static bool IsSortedNoRepeats(string text)
{
    if (text.Length == 0)
    {
        return true;
    }
    char current = text[0];
    for (int i=1; i < text.Length; i++)
    {
        char next = text[i];
        if (next <= current)
        {
            return false;
        }
        current = next;
    }
    return true;
}

如果您不介意重复使用索引器,请使用更短的替代方法:

public static bool IsSortedNoRepeats(string text)
{
    for (int i=1; i < text.Length; i++)
    {
        if (text[i] <= text[i-1])
        {
            return false;
        }
    }
    return true;
}

编辑:好的,在“频率”方面,我会把问题转过来。我仍然会假设字符串已经排序,所以我们想知道的是最长运行的长度。如果没有重复,则最长的运行长度将为0(对于空字符串)或1(对于非空字符串)。否则,它将是2或更多。

首先是特定于字符串的版本:

public static int LongestRun(string text)
{
    if (text.Length == 0)
    {
        return 0;
    }
    char current = text[0];
    int currentRun = 1;
    int bestRun = 0;

    for (int i=1; i < text.Length; i++)
    {
        if (current != text[i])
        {
            bestRun = Math.Max(currentRun, bestRun);
            currentRun = 0;
            current = text[i];
        }
        currentRun++;
    }
    // It's possible that the final run is the best one
    return Math.Max(currentRun, bestRun);
}

现在我们也可以将其作为IEnumerable<T>上的常规扩展方法:

public static int LongestRun(this IEnumerable<T> source)
{
    bool first = true;
    T current = default(T);
    int currentRun = 0;
    int bestRun = 0;

    foreach (T element in source)
    {
        if (first || !EqualityComparer<T>.Default(element, current))
        {
            first = false;
            bestRun = Math.Max(currentRun, bestRun);
            currentRun = 0;
            current = element;
        }
    }
    // It's possible that the final run is the best one
    return Math.Max(currentRun, bestRun);
}

然后你可以打电话给"AABCD".LongestRun()

答案 2 :(得分:8)

这将很快告诉你如果字符串包含重复项:

bool containsDups = "ABCDEA".Length != s.Distinct().Count();

它只是根据原始长度检查不同字符的数量。如果它们不同,你就有重复......

编辑:我想这并不会照顾你在编辑中注意到的重复频率......但是其他一些建议已经解决了,所以我不会发布代码,因为我注意到其中一些代码已经为您提供了一个相当优雅的解决方案。我特别喜欢使用LINQ扩展的Joe实现。

答案 3 :(得分:7)

由于您使用的是3.5,因此可以在一个LINQ查询中执行此操作:

var results = stringInput
  .ToCharArray() // not actually needed, I've left it here to show what's actually happening
  .GroupBy(c=>c)
  .Where(g=>g.Count()>1)
  .Select(g=>new {Letter=g.First(),Count=g.Count()})
;

对于在输入中出现多次的每个字符,这将为您提供字符和出现次数。

答案 4 :(得分:6)

我认为实现这一目标的最简单方法是使用这个简单的正则表达式

bool foundMatch = false;
foundMatch = Regex.IsMatch(yourString, @"(\w)\1");

如果您需要有关比赛的更多信息(开始,长度等)

        Match match = null;
    string testString = "ABCDE AABCD";
    match = Regex.Match(testString, @"(\w)\1+?");
    if (match.Success)
    {
        string matchText = match.Value; // AA
        int matchIndnex = match.Index;  // 6
        int matchLength = match.Length; // 2
    }

答案 5 :(得分:3)

更新现在,您需要一组计数器来维持计数。

保留一个位数组,其中一位代表一个唯一的字符。遇到一个角色时打开它,然后在字符串上运行一次。位数组索引和字符集的映射由您决定。如果您已经看到特定位已打开,请中断。

答案 6 :(得分:2)

/(.).*\1/

(或者你的正则表达式库的语法中的等价物)

效率最高,因为它可能会回溯到字符串中的每个字符,然后再次向前扫描。我通常不提倡正则表达式。但如果你想要简洁......

答案 7 :(得分:2)

如下:

string strString = "AA BRA KA DABRA";

var grp = from c in strString.ToCharArray() 
        group c by c into m
        select new { Key = m.Key, Count = m.Count() };

foreach (var item in grp)
{
    Console.WriteLine(
        string.Format("Character:{0} Appears {1} times", 
        item.Key.ToString(), item.Count));
}

答案 8 :(得分:1)

我开始在网上寻找一些信息,我得到了以下解决方案。

string input = "aaaaabbcbbbcccddefgg";
        char[] chars = input.ToCharArray();
        Dictionary<char, int> dictionary = new Dictionary<char,int>();

        foreach (char c in chars)
        {
            if (!dictionary.ContainsKey(c))
            {
                dictionary[c] = 1; //
            }
            else
            {
                dictionary[c]++;
            }
        }

        foreach (KeyValuePair<char, int> combo in dictionary)
        {
            if (combo.Value > 1) //If the vale of the key is greater than 1 it means the letter is repeated
            {
                Console.WriteLine("Letter " + combo.Key + " " + "is repeated " + combo.Value.ToString() + " times");
            }

        }

我希望它有所帮助,我接受了一次面试,面试官要求我解决这个问题,我理解这是一个常见的问题。

答案 9 :(得分:0)

如果没有订单可以使用,您可以使用字典来保留计数:

String input = "AABCD";
var result = new Dictionary<Char, int>(26);
var chars = input.ToCharArray();
foreach (var c in chars)
{
    if (!result.ContainsKey(c))
    {
        result[c] = 0; // initialize the counter in the result
    }
    result[c]++;
}

foreach (var charCombo in result)
{
    Console.WriteLine("{0}: {1}",charCombo.Key, charCombo.Value);   
}

答案 10 :(得分:0)

Jon描述的哈希解决方案可能是最好的。您可以使用HybridDictionary,因为它适用于小型和大型数据集。字母是关键,值是频率。 (每次添加失败时更新频率,或者HybridDictionary为.Contains(键)返回true)