Wystąpienia podciągu w ciągu


122

Dlaczego poniższy algorytm nie zatrzymuje się dla mnie? (str to ciąg, w którym szukam, findStr to ciąg, który próbuję znaleźć)

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while (lastIndex != -1) {
    lastIndex = str.indexOf(findStr,lastIndex);

    if( lastIndex != -1)
        count++;

    lastIndex += findStr.length();
}

System.out.println(count);

8
Zrobiliśmy naprawdę dobry w Udacity: użyliśmy newSTR = str.replace (findStr, ""); i zwrócił count = ((str.length () - newSTR.length ()) / findStr.length ());
SolarLunix

Podobne pytanie do postaci: stackoverflow.com/q/275944/873282
koppor

Czy nie chcesz również uwzględniać przypadku, w którym prefiks ciągu wyszukiwania jest jego sufiksem? W takim przypadku nie sądzę, aby którakolwiek z sugerowanych odpowiedzi zadziałała. oto przykład. W takim przypadku potrzebny byłby bardziej złożony algorytm, taki jak Knuth Morris Pratt (KMP), który jest zakodowany w książce CLRS
Sid,

nie zatrzymuje się dla Ciebie, ponieważ po osiągnięciu warunku 'stop' (lastIndex == -1) resetujesz go, zwiększając wartość lastIndex (lastIndex + = findStr.length ();)
Legna

Odpowiedzi:


83

Ostatnia linijka stwarzała problem. lastIndexnigdy nie byłby na -1, więc byłaby nieskończona pętla. Można to naprawić, przenosząc ostatnią linię kodu do bloku if.

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while(lastIndex != -1){

    lastIndex = str.indexOf(findStr,lastIndex);

    if(lastIndex != -1){
        count ++;
        lastIndex += findStr.length();
    }
}
System.out.println(count);

121
Ta odpowiedź jest dokładną kopią postu, który zrobiłem godzinę wcześniej;)
Olivier

8
Zwróć uwagę, że może to lub nie zwrócić oczekiwanego wyniku. Z podłańcuchem „aa” i ciągiem znaków do wyszukiwania „aaa” oczekiwana liczba wystąpień może wynosić jeden (zwracana przez ten kod), ale może też wynosić dwa (w tym przypadku będziesz potrzebować „lastIndex ++” zamiast „lastIndex + = findStr.length () ") w zależności od tego, czego szukasz.
Stanislav Kniazev

@olivier tego nie widział ... :( @stan to absolutnie poprawne ... właśnie naprawiałem kod w problemie ... chyba zależy to od tego, co bobcom ma na myśli przez liczbę wystąpień w ciągu ...
codebreach

1
Kiedy ludzie nauczą się zawijać takie rzeczy w statycznej metodzie kopiuj i wklej? Zobacz moją odpowiedź poniżej, jest również bardziej zoptymalizowana.
mmm

1
Morał jest taki, że jeśli zamierzasz napisać odpowiedź, najpierw sprawdź, czy ktoś inny napisał już dokładnie tę samą odpowiedź. Naprawdę nie ma żadnej korzyści z dwukrotnego wyświetlenia tej samej odpowiedzi, niezależnie od tego, czy Twoja odpowiedź została skopiowana, czy napisana niezależnie.
Dawood ibn Kareem,

193

Co powiesz na użycie StringUtils.countMatches z Apache Commons Lang?

String str = "helloslkhellodjladfjhello";
String findStr = "hello";

System.out.println(StringUtils.countMatches(str, findStr));

To daje:

3

9
Bez względu na to, jak słuszna jest ta sugestia, nie można jej zaakceptować jako rozwiązania, ponieważ nie odpowiada na pytanie OP
kommradHomer

3
Czy to jest przestarzałe, czy coś ... moje IDE nie rozpoznaje
Vamsi Pavan Mahesh,

@VamsiPavanMahesh StringUtils to biblioteka Apache Commons. Sprawdź tutaj: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
Anup

Ta odpowiedź jest kopią odpowiedzi Petera Lawreya dzień wcześniej (patrz poniżej).
Zon

StringUtilsnie ma countMatchesmetody.
bluzka w kratę

117

Twój lastIndex += findStr.length();został umieszczony poza nawiasami, powodując nieskończoną pętlę (gdy nie znaleziono żadnego wystąpienia, lastIndex zawsze miał findStr.length()).

Oto poprawiona wersja:

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while (lastIndex != -1) {

    lastIndex = str.indexOf(findStr, lastIndex);

    if (lastIndex != -1) {
        count++;
        lastIndex += findStr.length();
    }
}
System.out.println(count);

92

Krótsza wersja. ;)

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
System.out.println(str.split(findStr, -1).length-1);

8
return haystack.split(Pattern.quote(needle), -1).length - 1;jeśli na przykładneedle=":)"
Mr_and_Mrs_D

2
@lOranger Bez ,-1it pozostawi końcowe dopasowania.
Peter Lawrey

3
Ups, dzięki, dobrze wiedzieć! To nauczy mnie czytać małe wersety w javadoc ...
Laurent Grégoire

4
Miły! Ale obejmuje tylko mecze, które się nie pokrywają, prawda? Np. Dopasowanie „aa” do „aaa” zwróci 1, a nie 2? Oczywiście uwzględnienie pokrywających się lub nienakładających się dopasowań jest zarówno prawidłowe, jak i zależne od wymagań użytkownika (być może flaga wskazująca nakładające się liczby, tak / nie)?
Cornel Masson

2
-1 .. spróbuj uruchomić to na „aaaa” i „aa” .. prawidłowa odpowiedź to 3, a nie 2.
Kalyanaraman Santhanam

79

Czy naprawdę musisz sam poradzić sobie z dopasowywaniem? Zwłaszcza jeśli potrzebujesz tylko liczby wystąpień, wyrażenia regularne są bardziej uporządkowane:

String str = "helloslkhellodjladfjhello";
Pattern p = Pattern.compile("hello");
Matcher m = p.matcher(str);
int count = 0;
while (m.find()){
    count +=1;
}
System.out.println(count);     

1
To NIE znajdzie znaków specjalnych, znajdzie 0 zliczeń dla ciągów poniżej: String str = "hel+loslkhel+lodjladfjhel+lo"; Pattern p = Pattern.compile("hel+lo");
Ben

13
tak, będzie, jeśli poprawnie wyrazisz swoje wyrażenie regularne. spróbuj Pattern.compile("hel\\+lo");na +znak ma specjalne znaczenie w regex i musi być uciekł.
Jean

4
Jeśli to, czego szukasz, to wziąć dowolny ciąg i użyć go jako dokładnego dopasowania z ignorowaniem wszystkich specjalnych znaków wyrażenia regularnego, Pattern.quote(str)to twój przyjaciel!
Mike Furtak

2
to nie działa dla „aaa”, gdy str = „aaaaaa”. Są 4 odpowiedzi, ale twoja daje 2
Pujan Srivastava

To rozwiązanie nie działa w tym przypadku: str = "To jest test \\ n \\ r string", subStr = "\\ r", pokazuje 0 wystąpień.
Maksym Ovsianikov

19

Jestem bardzo zaskoczony, że nikt nie wspomniał o tej jednej wkładce. Jest prosty, zwięzły i działa nieco lepiej niżstr.split(target, -1).length-1

public static int count(String str, String target) {
    return (str.length() - str.replace(target, "").length()) / target.length();
}

Powinna być najlepsza odpowiedź. Dziękuję Ci!
lakam99

12

Oto jest, opakowany w ładną metodę wielokrotnego użytku:

public static int count(String text, String find) {
        int index = 0, count = 0, length = find.length();
        while( (index = text.indexOf(find, index)) != -1 ) {                
                index += length; count++;
        }
        return count;
}

8
String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while((lastIndex = str.indexOf(findStr, lastIndex)) != -1) {
     count++;
     lastIndex += findStr.length() - 1;
}
System.out.println(count);

na końcu liczby pętli wynosi 3; mam nadzieję, że to pomoże


5
Kod zawiera błąd. Jeśli szukamy pojedynczego znaku, findStr.length() - 1zwraca 0 i znajdujemy się w niekończącym się cyklu.
Jan Bodnar

6

Wiele z podanych odpowiedzi kończy się niepowodzeniem w jednym lub kilku z:

  • Wzory o dowolnej długości
  • Pokrywające się dopasowania (np. Liczenie „232” w „23232” lub „aa” w „aaa”)
  • Metaznaki wyrażeń regularnych

Oto co napisałem:

static int countMatches(Pattern pattern, String string)
{
    Matcher matcher = pattern.matcher(string);

    int count = 0;
    int pos = 0;
    while (matcher.find(pos))
    {
        count++;
        pos = matcher.start() + 1;
    }

    return count;
}

Przykładowe połączenie:

Pattern pattern = Pattern.compile("232");
int count = countMatches(pattern, "23232"); // Returns 2

Jeśli chcesz wyszukiwać bez wyrażeń regularnych, po prostu skompiluj odpowiednio swój wzorzec z LITERALflagą:

Pattern pattern = Pattern.compile("1+1", Pattern.LITERAL);
int count = countMatches(pattern, "1+1+1"); // Returns 2

Tak ... zaskoczony, że nie ma czegoś takiego w Apache StringUtils.
mike gryzoń

6
public int countOfOccurrences(String str, String subStr) {
  return (str.length() - str.replaceAll(Pattern.quote(subStr), "").length()) / subStr.length();
}

Dobra odpowiedź. Czy możesz dodać kilka uwag na temat tego, jak to działa?
santhosh kumar

Jasne, str - to nasz ciąg źródłowy, subStr - to podciąg. Celem jest obliczenie liczby wystąpień subStr w str. Aby to zrobić, używamy wzoru: (ab) / c, gdzie a - długość str, b - długość str bez wszystkich wystąpień subStr (w tym celu usuwamy wszystkie wystąpienia subStr z str), c - długość subStr . Tak więc, w zasadzie wyodrębniamy z length str - length str bez wszystkich subStr, a następnie dzielimy wynik przez długość subStr. Daj mi znać, jeśli masz inne pytania.
Maksym Ovsianikov

Santhosh, nie ma za co! Ważną częścią jest użycie Pattern.quote dla subStr, w przeciwnym razie in może się nie powieść w niektórych przypadkach, takich jak ten: str = "To jest test \\ n \\ r string", subStr = "\\ r". Niektóre podobne odpowiedzi podane tutaj nie używają Patternu, więc w takich przypadkach zawiodą.
Maksym Ovsianikov

Nie ma powodu dla wyrażenia regularnego, użyj replace, nie replaceAll.
NateS

3

Zwiększaj lastIndexza każdym razem, gdy szukasz następnego wystąpienia.

W przeciwnym razie zawsze znajduje pierwszy podciąg (na pozycji 0).


3
public int indexOf(int ch,
                   int fromIndex)

Zwraca indeks w ramach tego ciągu pierwszego wystąpienia określonego znaku, rozpoczynając wyszukiwanie od określonego indeksu.

Więc twój lastindex wartość jest zawsze 0 i zawsze znajduje hello w ciągu.


2

Odpowiedź podana jako poprawna nie nadaje się do liczenia takich rzeczy, jak powroty linii i jest zbyt szczegółowa. Późniejsze odpowiedzi są lepsze, ale wszystko można osiągnąć po prostu za pomocą

str.split(findStr).length

Nie usuwa końcowych dopasowań, korzystając z przykładu w pytaniu.


1
Zostało to już omówione w innej odpowiedzi ; i ta odpowiedź też zrobiła to lepiej.
michaelb958 - GoFundMonica

1
Powinien to być komentarz do danej odpowiedzi, a nie inna odpowiedź.
james.garriss

2

Możesz liczbę wystąpień za pomocą wbudowanej funkcji bibliotecznej:

import org.springframework.util.StringUtils;
StringUtils.countOccurrencesOf(result, "R-")

1
Nie działa, należy określić używaną zależność.
Saikat

1

spróbuj dodać lastIndex+=findStr.length()na końcu pętli, w przeciwnym razie skończysz w nieskończonej pętli, ponieważ po znalezieniu podciągu próbujesz go znaleźć ponownie i ponownie z tej samej ostatniej pozycji.


1

Spróbuj tego. Zastępuje wszystkie dopasowania rozszerzeniem -.

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int numberOfMatches = 0;
while (str.contains(findStr)){
    str = str.replaceFirst(findStr, "-");
    numberOfMatches++;
}

A jeśli nie chcesz niszczyć swojego str, możesz stworzyć nowy ciąg o tej samej zawartości:

String str = "helloslkhellodjladfjhello";
String strDestroy = str;
String findStr = "hello";
int numberOfMatches = 0;
while (strDestroy.contains(findStr)){
    strDestroy = strDestroy.replaceFirst(findStr, "-");
    numberOfMatches++;
}

Po wykonaniu tego bloku będą to twoje wartości:

str = "helloslkhellodjladfjhello"
strDestroy = "-slk-djladfj-"
findStr = "hello"
numberOfMatches = 3

1

Zgodnie z sugestią @Mr_and_Mrs_D:

String haystack = "hellolovelyworld";
String needle = "lo";
return haystack.split(Pattern.quote(needle), -1).length - 1;

1

Na podstawie istniejących odpowiedzi chciałbym dodać „krótszą” wersję bez warunku if:

String str = "helloslkhellodjladfjhello";
String findStr = "hello";

int count = 0, lastIndex = 0;
while((lastIndex = str.indexOf(findStr, lastIndex)) != -1) {
    lastIndex += findStr.length() - 1;
    count++;
}

System.out.println(count); // output: 3

ten bierze pod uwagę, czy ciąg się powtarza, na przykład jeśli szukasz ciągu „xx” w ciągu „xxx”.
tCoe

1

Oto zaawansowana wersja do zliczania, ile razy token wystąpił w ciągu wprowadzonym przez użytkownika:

public class StringIndexOf {

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);

        System.out.println("Enter a sentence please: \n");
        String string = scanner.nextLine();

        int atIndex = 0;
        int count = 0;

        while (atIndex != -1)
        {
            atIndex = string.indexOf("hello", atIndex);

            if(atIndex != -1)
            {
                count++;
                atIndex += 5;
            }
        }

        System.out.println(count);
    }

}

1

Poniższa metoda pokazuje, ile razy podciąg powtórzył się w całym ciągu. Mam nadzieję, że wykorzystasz w pełni: -

    String searchPattern="aaa"; // search string
    String str="aaaaaababaaaaaa"; // whole string
    int searchLength = searchPattern.length(); 
    int totalLength = str.length(); 
    int k = 0;
    for (int i = 0; i < totalLength - searchLength + 1; i++) {
        String subStr = str.substring(i, searchLength + i);
        if (subStr.equals(searchPattern)) {
           k++;
        }

    }

0

tutaj jest inne rozwiązanie bez użycia regexp / patterns / matcherów lub nawet bez użycia StringUtils.

String str = "helloslkhellodjladfjhelloarunkumarhelloasdhelloaruhelloasrhello";
        String findStr = "hello";
        int count =0;
        int findStrLength = findStr.length();
        for(int i=0;i<str.length();i++){
            if(findStr.startsWith(Character.toString(str.charAt(i)))){
                if(str.substring(i).length() >= findStrLength){
                    if(str.substring(i, i+findStrLength).equals(findStr)){
                        count++;
                    }
                }
            }
        }
        System.out.println(count);

0

Jeśli potrzebujesz indeksu każdego podciągu w oryginalnym ciągu, możesz zrobić coś z indexOf w ten sposób:

 private static List<Integer> getAllIndexesOfSubstringInString(String fullString, String substring) {
    int pointIndex = 0;
    List<Integer> allOccurences = new ArrayList<Integer>();
    while(fullPdfText.indexOf(substring,pointIndex) >= 0){
       allOccurences.add(fullPdfText.indexOf(substring, pointIndex));
       pointIndex = fullPdfText.indexOf(substring, pointIndex) + substring.length();
    }
    return allOccurences;
}

0
public static int getCountSubString(String str , String sub){
int n = 0, m = 0, counter = 0, counterSub = 0;
while(n < str.length()){
  counter = 0;
  m = 0;
  while(m < sub.length() && str.charAt(n) == sub.charAt(m)){
    counter++;
    m++; n++;
  }
  if (counter == sub.length()){
    counterSub++;
    continue;
  }
  else if(counter > 0){
    continue;
  }
  n++;
}

return  counterSub;

}


to pytanie ma 8 lat i bez żadnego wskazania, dlaczego jest to lepsze rozwiązanie niż 22 innych opublikowanych rozwiązań, prawdopodobnie powinno zostać usunięte
Jason Wheeler

0

To rozwiązanie wypisuje całkowitą liczbę wystąpień danego podciągu w całym ciągu, a także obejmuje przypadki, w których zachodzą na siebie dopasowania.

class SubstringMatch{
    public static void main(String []args){
        //String str = "aaaaabaabdcaa";
        //String sub = "aa";
        //String str = "caaab";
        //String sub = "aa";
        String str="abababababaabb";
        String sub = "bab";

        int n = str.length();
        int m = sub.length();

        // index=-1 in case of no match, otherwise >=0(first match position)
        int index=str.indexOf(sub), i=index+1, count=(index>=0)?1:0;
        System.out.println(i+" "+index+" "+count);

        // i will traverse up to only (m-n) position
        while(index!=-1 && i<=(n-m)){   
            index=str.substring(i, n).indexOf(sub);
            count=(index>=0)?count+1:count;
            i=i+index+1;  
            System.out.println(i+" "+index);
        }
        System.out.println("count: "+count);
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.