Как бы вы посчитали вхождения строки (на самом деле символ) в строке? - PullRequest
775 голосов
/ 12 февраля 2009

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

На данный момент я собираюсь что-то вроде:

string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;

Но мне это совсем не нравится, любители?

Я действительно не хочу выкапывать RegEx для этого, не так ли?

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

Конечно, для строк , где длина> 1 ,

string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;

Ответы [ 30 ]

2 голосов
/ 29 марта 2013
            var conditionalStatement = conditionSetting.Value;

            //order of replace matters, remove == before =, incase of ===
            conditionalStatement = conditionalStatement.Replace("==", "~").Replace("!=", "~").Replace('=', '~').Replace('!', '~').Replace('>', '~').Replace('<', '~').Replace(">=", "~").Replace("<=", "~");

            var listOfValidConditions = new List<string>() { "!=", "==", ">", "<", ">=", "<=" };

            if (conditionalStatement.Count(x => x == '~') != 1)
            {
                result.InvalidFieldList.Add(new KeyFieldData(batch.DECurrentField, "The IsDoubleKeyCondition does not contain a supported conditional statement. Contact System Administrator."));
                result.Status = ValidatorStatus.Fail;
                return result;
            }

Требуется сделать что-то похожее на проверку условных операторов из строки.

Заменил то, что я искал, одним символом и посчитал количество экземпляров одного символа.

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

2 голосов
/ 25 января 2013
string source = "/once/upon/a/time/";
int count = 0, n = 0;
while ((n = source.IndexOf('/', n) + 1) != 0) count++;

Вариант ответа Ричарда Уотсона: немного быстрее с повышением эффективности, чем больше раз встречается символ в строке, тем меньше кода!

Хотя я должен сказать, что без тщательного тестирования каждого сценария, я увидел очень значительное улучшение скорости с помощью:

int count = 0;
for (int n = 0; n < source.Length; n++) if (source[n] == '/') count++;
1 голос
/ 17 ноября 2017

Для случая строкового разделителя (не для случая символа, как говорит субъект):
строка источника = "@@@ один раз @@@ после @@@ a @@@ time @@@";
int count = source.Split (new [] {"@@@"}, StringSplitOptions.RemoveEmptyEntries) .Length - 1;

Естественный разделитель исходного значения плаката ("/ Once / Upon / A / Time /") представляет собой символ '/', а ответы объясняют параметр source.Split (char []), хотя ...

1 голос
/ 09 декабря 2011
str="aaabbbbjjja";
int count = 0;
int size = str.Length;

string[] strarray = new string[size];
for (int i = 0; i < str.Length; i++)
{
    strarray[i] = str.Substring(i, 1);
}
Array.Sort(strarray);
str = "";
for (int i = 0; i < strarray.Length - 1; i++)
{

    if (strarray[i] == strarray[i + 1])
    {

        count++;
    }
    else
    {
        count++;
        str = str + strarray[i] + count;
        count = 0;
    }

}
count++;
str = str + strarray[strarray.Length - 1] + count;

Это для подсчета появления персонажа. Для этого примера выводом будет «a4b4j3»

1 голос
/ 29 мая 2015

Мой первоначальный дубль дал мне что-то вроде:

public static int CountOccurrences(string original, string substring)
{
    if (string.IsNullOrEmpty(substring))
        return 0;
    if (substring.Length == 1)
        return CountOccurrences(original, substring[0]);
    if (string.IsNullOrEmpty(original) ||
        substring.Length > original.Length)
        return 0;
    int substringCount = 0;
    for (int charIndex = 0; charIndex < original.Length; charIndex++)
    {
        for (int subCharIndex = 0, secondaryCharIndex = charIndex; subCharIndex < substring.Length && secondaryCharIndex < original.Length; subCharIndex++, secondaryCharIndex++)
        {
            if (substring[subCharIndex] != original[secondaryCharIndex])
                goto continueOuter;
        }
        if (charIndex + substring.Length > original.Length)
            break;
        charIndex += substring.Length - 1;
        substringCount++;
    continueOuter:
        ;
    }
    return substringCount;
}

public static int CountOccurrences(string original, char @char)
{
    if (string.IsNullOrEmpty(original))
        return 0;
    int substringCount = 0;
    for (int charIndex = 0; charIndex < original.Length; charIndex++)
        if (@char == original[charIndex])
            substringCount++;
    return substringCount;
}

Игла при подходе к стогу сена с использованием замены и деления дает 21+ секунд, тогда как для этого требуется около 15,2.

Редактировать после добавления бита, который добавит substring.Length - 1 к charIndex (как и должно быть), это на 11,6 секунды.

Редактировать 2: я использовал строку, в которой было 26 двухсимвольных строк, вот время, обновленное для того же образца текста:

Игла в стоге сена (версия OP): 7,8 секунды

Предлагаемый механизм: 4,6 секунды.

Редактировать 3: добавление односимвольного углового регистра заняло 1,2 секунды.

Редактировать 4: Для контекста: было использовано 50 миллионов итераций.

1 голос
/ 24 марта 2015

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

РЕДАКТИРОВАТЬ: ОК - поэтому этот вопрос SO заставил меня задуматься о том, как производительность нашей текущей реализации будет сочетаться с некоторыми решениями, представленными здесь. Я решил сделать небольшую оценку производительности и обнаружил, что наше решение в значительной степени соответствует производительности решения Ричарда Уотсона до тех пор, пока вы не начнете агрессивный поиск с большими строками (100 Кбайт +), большими подстроками (32 Кбайт +) ) и много встроенных повторений (10K +). На тот момент наше решение было примерно в 2-4 раза медленнее. Учитывая это и тот факт, что нам действительно нравится решение, представленное Ричардом Уотсоном, мы соответствующим образом реорганизовали наше решение. Я просто хотел сделать это доступным для всех, кто мог бы извлечь из этого пользу.

Наше оригинальное решение:

    /// <summary>
    /// Counts the number of occurrences of the specified substring within
    /// the current string.
    /// </summary>
    /// <param name="s">The current string.</param>
    /// <param name="substring">The substring we are searching for.</param>
    /// <param name="aggressiveSearch">Indicates whether or not the algorithm 
    /// should be aggressive in its search behavior (see Remarks). Default 
    /// behavior is non-aggressive.</param>
    /// <remarks>This algorithm has two search modes - aggressive and 
    /// non-aggressive. When in aggressive search mode (aggressiveSearch = 
    /// true), the algorithm will try to match at every possible starting 
    /// character index within the string. When false, all subsequent 
    /// character indexes within a substring match will not be evaluated. 
    /// For example, if the string was 'abbbc' and we were searching for 
    /// the substring 'bb', then aggressive search would find 2 matches 
    /// with starting indexes of 1 and 2. Non aggressive search would find 
    /// just 1 match with starting index at 1. After the match was made, 
    /// the non aggressive search would attempt to make it's next match 
    /// starting at index 3 instead of 2.</remarks>
    /// <returns>The count of occurrences of the substring within the string.</returns>
    public static int CountOccurrences(this string s, string substring, 
        bool aggressiveSearch = false)
    {
        // if s or substring is null or empty, substring cannot be found in s
        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(substring))
            return 0;

        // if the length of substring is greater than the length of s,
        // substring cannot be found in s
        if (substring.Length > s.Length)
            return 0;

        var sChars = s.ToCharArray();
        var substringChars = substring.ToCharArray();
        var count = 0;
        var sCharsIndex = 0;

        // substring cannot start in s beyond following index
        var lastStartIndex = sChars.Length - substringChars.Length;

        while (sCharsIndex <= lastStartIndex)
        {
            if (sChars[sCharsIndex] == substringChars[0])
            {
                // potential match checking
                var match = true;
                var offset = 1;
                while (offset < substringChars.Length)
                {
                    if (sChars[sCharsIndex + offset] != substringChars[offset])
                    {
                        match = false;
                        break;
                    }
                    offset++;
                }
                if (match)
                {
                    count++;
                    // if aggressive, just advance to next char in s, otherwise, 
                    // skip past the match just found in s
                    sCharsIndex += aggressiveSearch ? 1 : substringChars.Length;
                }
                else
                {
                    // no match found, just move to next char in s
                    sCharsIndex++;
                }
            }
            else
            {
                // no match at current index, move along
                sCharsIndex++;
            }
        }

        return count;
    }

А вот наше исправленное решение:

    /// <summary>
    /// Counts the number of occurrences of the specified substring within
    /// the current string.
    /// </summary>
    /// <param name="s">The current string.</param>
    /// <param name="substring">The substring we are searching for.</param>
    /// <param name="aggressiveSearch">Indicates whether or not the algorithm 
    /// should be aggressive in its search behavior (see Remarks). Default 
    /// behavior is non-aggressive.</param>
    /// <remarks>This algorithm has two search modes - aggressive and 
    /// non-aggressive. When in aggressive search mode (aggressiveSearch = 
    /// true), the algorithm will try to match at every possible starting 
    /// character index within the string. When false, all subsequent 
    /// character indexes within a substring match will not be evaluated. 
    /// For example, if the string was 'abbbc' and we were searching for 
    /// the substring 'bb', then aggressive search would find 2 matches 
    /// with starting indexes of 1 and 2. Non aggressive search would find 
    /// just 1 match with starting index at 1. After the match was made, 
    /// the non aggressive search would attempt to make it's next match 
    /// starting at index 3 instead of 2.</remarks>
    /// <returns>The count of occurrences of the substring within the string.</returns>
    public static int CountOccurrences(this string s, string substring, 
        bool aggressiveSearch = false)
    {
        // if s or substring is null or empty, substring cannot be found in s
        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(substring))
            return 0;

        // if the length of substring is greater than the length of s,
        // substring cannot be found in s
        if (substring.Length > s.Length)
            return 0;

        int count = 0, n = 0;
        while ((n = s.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
        {
            if (aggressiveSearch)
                n++;
            else
                n += substring.Length;
            count++;
        }

        return count;
    }
1 голос
/ 16 августа 2014

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

Самый быстрый способ - использовать однопоточный цикл for (если у вас версия .Net <4.0) или параллельный цикл for (если вы используете .Net> 4.0 с тысячами проверок).

Предполагая, что "ss" - это ваша строка поиска, "ch" - это ваш массив символов (если у вас есть более одного искомого символа), вот основная суть кода, который имел самый быстрый однопотоковый режим выполнения:

for (int x = 0; x < ss.Length; x++)
{
    for (int y = 0; y < ch.Length; y++)
    {
        for (int a = 0; a < ss[x].Length; a++ )
        {
        if (ss[x][a] == ch[y])
            //it's found. DO what you need to here.
        }
    }
}

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

1 голос
/ 14 июня 2014
string s = "HOWLYH THIS ACTUALLY WORKSH WOWH";
int count = 0;
for (int i = 0; i < s.Length; i++)
   if (s[i] == 'H') count++;

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

1 голос
/ 04 апреля 2012
string Name = "Very good nice one is very good but is very good nice one this is called the term";
bool valid=true;
int count = 0;
int k=0;
int m = 0;
while (valid)
{
    k = Name.Substring(m,Name.Length-m).IndexOf("good");
    if (k != -1)
    {
        count++;
        m = m + k + 4;
    }
    else
        valid = false;
}
Console.WriteLine(count + " Times accures");
0 голосов
/ 30 мая 2019

с использованием System.Linq;

int CountOf => "A :: B C :: D" .Split ("::"). Длина - 1;

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