Тестирование на повторяющиеся символы в строке - PullRequest
8 голосов
/ 06 мая 2009

Я делаю некоторую работу со строками, и у меня есть сценарий, в котором мне нужно определить, содержит ли строка (обычно маленькая <10 символов) повторяющиеся символы. </p>

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

Я могу перебрать string.ToCharArray () и проверить каждый символ на предмет всех остальных символов в char [], но я чувствую, что упускаю что-то очевидное .... может, мне просто нужен кофе. Кто-нибудь может помочь?

EDIT:

Строка будет отсортирована, поэтому порядок не важен, поэтому ABCDA => AABCD

Частота повторений также важна, поэтому мне нужно знать, является ли повтор парным или триплетным и т. Д.

Ответы [ 11 ]

16 голосов
/ 06 мая 2009

Если строка отсортирована, вы можете просто запомнить каждый символ по очереди и убедиться, что следующий символ никогда не будет идентичен последнему символу.

Кроме этого, для строк длиной до десяти символов простое тестирование каждого символа по отношению ко всем остальным, вероятно, происходит быстрее или быстрее, чем большинство других вещей. Битовый вектор, как предлагает другой комментатор, может быть быстрее (помогает, если у вас небольшой набор допустимых символов.)

Бонус: вот отличное решение LINQ для реализации функциональности Джона:

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

Итак, хорошо, это не очень быстро! У вас есть проблемы с этим?!

: -)

9 голосов
/ 06 мая 2009

Если строка короткая, то зацикливание и тестирование могут быть самым простым и эффективным способом. Я имею в виду, что вы могли бы создать хэш-набор (на любой платформе, которую вы используете) и выполнить итерацию по символам, если произошел сбой, если символ уже есть в наборе, и добавить его в набор в противном случае, но это только чтобы обеспечить какую-либо выгоду, когда строки длиннее.

РЕДАКТИРОВАТЬ: Теперь, когда мы знаем, что это отсортировано, Ответ mquander является лучшим ИМО. Вот реализация:

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().

8 голосов
/ 06 мая 2009

Это очень быстро скажет вам , если строка содержит дубликаты:

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

Он просто проверяет количество отдельных символов по сравнению с исходной длиной. Если они разные, у вас есть дубликаты ...

Редактировать: Я думаю, что это не учитывает частоту дупликов, которые вы отметили в своем редактировании, хотя ... но некоторые другие предложения здесь уже позаботятся об этом, поэтому я не буду публиковать код, как я отмечаю, некоторые из них уже дают вам достаточно элегантное решение. Мне особенно нравится реализация Джо с использованием расширений LINQ.

7 голосов
/ 06 мая 2009

Поскольку вы используете 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()})
;

Для каждого символа, который появляется более одного раза на входе, это даст вам символ и количество вхождений.

6 голосов
/ 06 мая 2009

Я думаю, что самый простой способ достичь этого - использовать это простое регулярное выражение

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
    }
3 голосов
/ 06 мая 2009

Обновление Теперь вам нужен массив счетчиков, чтобы поддерживать счет.

Сохранять битовый массив, один бит которого представляет собой уникальный символ. Включите бит, когда вы столкнетесь с персонажем, и бегите по строке один раз. Отображение индекса массива битов и набора символов зависит от вас. Перерыв, если вы видите, что определенный бит уже включен.

2 голосов
/ 06 мая 2009

Как насчет чего-то вроде:

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));
}
2 голосов
/ 06 мая 2009
/(.).*\1/

(или любой другой эквивалент в синтаксисе вашей библиотеки регулярных выражений)

Не самый эффективный, так как он, вероятно, вернется к каждому символу в строке, а затем снова выполнит сканирование вперед. И я обычно не поддерживаю регулярные выражения. Но если вы хотите краткости ...

1 голос
/ 22 марта 2016

Я начал искать информацию в сети, и я нашел следующее решение.

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");
            }

        }

Надеюсь, это поможет, у меня было собеседование, во время которого интервьюер попросил меня решить эту проблему, и я понимаю, что это обычный вопрос.

0 голосов
/ 06 мая 2009

Решение для хэша, которое описывал Джон, вероятно, лучшее. Вы можете использовать HybridDictionary, так как он хорошо работает с маленькими и большими наборами данных. Где буква - это ключ, а значение - частота. (Обновляйте частоту каждый раз, когда происходит сбой добавления или HybridDictionary возвращает true для .Contains (ключ))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...