การเกิดสตริงย่อยในสตริง


122

เหตุใดอัลกอริทึมต่อไปนี้จึงไม่หยุดชะงักสำหรับฉัน (str คือสตริงที่ฉันกำลังค้นหา findStr คือสตริงที่ฉันพยายามค้นหา)

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
เราทำได้ดีมากใน Udacity: เราใช้ newSTR = str.replace (findStr, ""); และส่งคืน count = ((str.length () - newSTR.length ()) / findStr.length ());
SolarLunix

คำถามที่คล้ายกันสำหรับตัวละคร: stackoverflow.com/q/275944/873282
koppor

คุณไม่ต้องการพิจารณากรณีที่คำนำหน้าของสตริงการค้นหาเป็นส่วนต่อท้ายหรือไม่? ในกรณีนี้ฉันไม่คิดว่าคำตอบที่แนะนำจะใช้ได้ผล นี่คือตัวอย่าง ในกรณีนี้คุณจะต้องมีอัลกอริทึมที่ซับซ้อนมากขึ้นเช่น Knuth Morris Pratt (KMP) ซึ่งเขียนโค้ดไว้ในหนังสือ CLRS
Sid

มันไม่ได้หยุดสำหรับคุณเพราะหลังจากถึงเงื่อนไข 'หยุด' ของคุณ (lastIndex == -1) คุณรีเซ็ตโดยการเพิ่มค่าของ lastIndex (lastIndex + = findStr.length ();)
Legna

คำตอบ:


83

บรรทัดสุดท้ายกำลังสร้างปัญหา lastIndexจะไม่อยู่ที่ -1 ดังนั้นจะมีการวนซ้ำไม่สิ้นสุด ซึ่งสามารถแก้ไขได้โดยการย้ายบรรทัดสุดท้ายของโค้ดไปที่บล็อก 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
คำตอบนี้เป็นสำเนาที่ถูกต้องของโพสต์ที่ฉันทำเมื่อหนึ่งชั่วโมงก่อน;)
Olivier

8
โปรดทราบว่าสิ่งนี้อาจส่งคืนหรือไม่ได้ผลลัพธ์ที่คาดหวัง ด้วยสตริงย่อย "aa" และสตริงเพื่อค้นหา "aaa" จำนวนครั้งที่เกิดขึ้นที่คาดไว้อาจเป็นหนึ่งรายการ (ส่งคืนโดยรหัสนี้) แต่อาจเป็นสองรายการเช่นกัน (ในกรณีนี้คุณจะต้องใช้ "lastIndex ++" แทน "lastIndex + = findStr.length () ") ขึ้นอยู่กับสิ่งที่คุณกำลังมองหา
Stanislav Kniazev

@olivier ไม่เห็นว่า ... :( @stan นั่นถูกต้องแล้ว ... ฉันแค่แก้ไขรหัสในปัญหา ... เดาว่ามันขึ้นอยู่กับความหมายของ Bobcom ตามจำนวนครั้งที่เกิดขึ้นในสตริง ...
codebreach

1
เมื่อใดผู้คนจะเรียนรู้การห่อสิ่งต่างๆเช่นนี้ในวิธีการคัดลอกและวางแบบคงที่ ดูคำตอบของฉันด้านล่างซึ่งได้รับการปรับให้เหมาะสมยิ่งขึ้น
mmm

1
หลักศีลธรรมก็คือถ้าคุณตั้งใจจะเขียนคำตอบให้ตรวจสอบก่อนว่ามีคนอื่นเขียนคำตอบเดียวกันหรือไม่ ไม่มีประโยชน์จริงๆที่คำตอบเดียวกันจะปรากฏขึ้นสองครั้งไม่ว่าคำตอบของคุณจะถูกคัดลอกหรือเขียนขึ้นโดยอิสระ
Dawood ibn Kareem

192

วิธีการใช้StringUtils.countMatchesจาก Apache Commons Lang

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

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

ผลลัพธ์นั้น:

3

9
ไม่ว่าคำแนะนำนี้จะถูกต้องแค่ไหนก็ไม่สามารถยอมรับได้ว่าเป็นวิธีแก้ปัญหาเนื่องจากไม่ได้ตอบคำถามของ OP
kommradHomer

3
นี่เลิกใช้แล้วหรืออะไร .. IDE ของฉันไม่รู้จัก
Vamsi Pavan Mahesh

@VamsiPavanMahesh StringUtils เป็นไลบรารีของ Apache Commons ตรวจสอบที่นี่: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
Anup

คำตอบนี้เป็นสำเนาคำตอบของ Peter Lawrey หนึ่งวันก่อนหน้านี้ (ดูด้านล่าง)
Zon

StringUtilsไม่มีcountMatchesวิธีการ
plaidshirt

117

ของคุณlastIndex += findStr.length();ถูกวางไว้นอกวงเล็บทำให้เกิดการวนซ้ำที่ไม่มีที่สิ้นสุด (เมื่อไม่พบเหตุการณ์ที่เกิดขึ้น lastIndex จะเป็นเสมอfindStr.length())

นี่คือเวอร์ชันคงที่:

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

รุ่นที่สั้นกว่า ;)

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;ถ้าเช่นneedle=":)"
Mr_and_Mrs_D

2
@lOranger หากไม่มี,-1มันจะทิ้งการแข่งขันต่อท้าย
Peter Lawrey

3
อุ๊ยขอบคุณที่ได้รู้จัก! สิ่งนี้จะสอนให้ฉันอ่านบรรทัดเล็ก ๆ ใน javadoc ...
Laurent Grégoire

4
ดี! แต่รวมเฉพาะการจับคู่ที่ไม่ทับซ้อนกันใช่หรือไม่? เช่นการจับคู่ "aa" ใน "aaa" จะส่งคืน 1 ไม่ใช่ 2? แน่นอนว่าการจับคู่แบบซ้อนทับหรือไม่ทับซ้อนกันนั้นใช้ได้และขึ้นอยู่กับความต้องการของผู้ใช้ (อาจเป็นแฟล็กเพื่อระบุจำนวนที่ทับซ้อนกันใช่ / ไม่ใช่)?
Cornel Masson

2
-1 .. ลองรันบน "aaaa" และ "aa" .. คำตอบที่ถูกต้องคือ 3 ไม่ใช่ 2
Kalyanaraman Santhanam

79

คุณต้องจัดการจับคู่ด้วยตัวเองจริงๆหรือ? โดยเฉพาะอย่างยิ่งถ้าสิ่งที่คุณต้องการคือจำนวนครั้งที่เกิดขึ้นนิพจน์ทั่วไปจะเป็นระเบียบเรียบร้อยกว่า:

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
ไม่พบอักขระพิเศษจะพบ 0 นับสำหรับสตริงด้านล่าง: String str = "hel+loslkhel+lodjladfjhel+lo"; Pattern p = Pattern.compile("hel+lo");
เบ็น

13
ใช่ถ้าคุณแสดงนิพจน์ทั่วไปอย่างถูกต้อง ลองกับสัญญาณที่มีความหมายพิเศษใน regex และความต้องการที่จะหลบหนี Pattern.compile("hel\\+lo");+
ฌอง

4
หากสิ่งที่คุณกำลังมองหาคือการใช้สตริงตามอำเภอใจและใช้เป็นการจับคู่แบบตรงกับอักขระนิพจน์ทั่วไปพิเศษทั้งหมดที่ละเว้นPattern.quote(str)เพื่อนของคุณ!
Mike Furtak

2
สิ่งนี้ใช้ไม่ได้กับ "aaa" เมื่อ str = "aaaaaa" มี 4 คำตอบ แต่คุณให้ 2
Pujan Srivastava

วิธีนี้ใช้ไม่ได้กับกรณีนี้: str = "นี่คือการทดสอบ \\ n \\ r string", subStr = "\\ r" ซึ่งจะแสดงรายการ 0 ครั้ง
Maksym Ovsianikov

19

ฉันแปลกใจมากที่ไม่มีใครพูดถึงซับนี้ ง่ายกระชับและทำงานได้ดีกว่าเล็กน้อยstr.split(target, -1).length-1

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

ควรเป็นคำตอบอันดับต้น ๆ ขอบคุณ!
lakam99

12

นี่คือวิธีการที่ดีและใช้ซ้ำได้:

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

ในตอนท้ายของการนับลูปคือ 3 หวังว่ามันจะช่วยได้


5
รหัสมีข้อผิดพลาด หากเราค้นหาอักขระตัวเดียวfindStr.length() - 1ผลตอบแทนเป็น 0 และเราอยู่ในวงจรที่ไม่มีที่สิ้นสุด
ม.ค. Bodnar

6

คำตอบที่ได้รับจำนวนมากล้มเหลวในหนึ่งข้อหรือมากกว่า:

  • รูปแบบของความยาวโดยพลการ
  • การจับคู่ที่ทับซ้อนกัน (เช่นการนับ "232" ใน "23232" หรือ "aa" ใน "aaa")
  • อักขระเมตาของนิพจน์ทั่วไป

นี่คือสิ่งที่ฉันเขียน:

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

ตัวอย่างการโทร:

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

หากคุณต้องการค้นหานิพจน์ทั่วไปเพียงรวบรวมรูปแบบของคุณให้เหมาะสมกับLITERALแฟล็ก:

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

ใช่ ... แปลกใจที่ไม่มีสิ่งนี้ใน Apache StringUtils
หนูไมค์

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

คำตอบที่ดี. คุณสามารถเพิ่มหมายเหตุเกี่ยวกับวิธีการทำงานได้หรือไม่?
santhosh kumar

แน่นอนว่า str - คือสตริงต้นทางของเรา subStr - เป็นสตริงย่อย เป้าหมายคือการคำนวณจำนวนครั้งที่เกิดขึ้นของ subStr ใน str ในการทำเช่นนี้เราใช้สูตร: (ab) / c โดยที่ a - ความยาวของ str, b - ความยาวของ str โดยไม่มีการเกิด subStr ทั้งหมด (เราลบการเกิด subStr ทั้งหมดออกจาก str สำหรับสิ่งนี้), c - ความยาวของ subStr . โดยพื้นฐานแล้วเราแยกจากความยาวของ str - ความยาวของ str โดยไม่มี subStr ทั้งหมดแล้วเราหารผลลัพธ์ตามความยาวของ subStr โปรดแจ้งให้เราทราบหากคุณมีคำถามอื่น ๆ
Maksym Ovsianikov

Santhosh ยินดีต้อนรับ! ส่วนที่สำคัญคือการใช้ Pattern.quote สำหรับ subStr มิฉะนั้นอาจล้มเหลวในบางกรณีเช่นนี้: str = "นี่คือการทดสอบ \\ n \\ r string", subStr = "\\ r" คำตอบที่คล้ายกันบางคำที่ให้ไว้ที่นี่ไม่ได้ใช้ Pattern ดังนั้นจะล้มเหลวในกรณีเช่นนี้
Maksym Ovsianikov

มีเหตุผลสำหรับ regex ใช้ไม่เป็นไม่ได้replace replaceAll
NateS

3

เพิ่มขึ้นlastIndexทุกครั้งที่คุณมองหาเหตุการณ์ต่อไป

มิฉะนั้นจะเป็นการค้นหาสตริงย่อยแรกเสมอ (ที่ตำแหน่ง 0)


3
public int indexOf(int ch,
                   int fromIndex)

ส่งคืนดัชนีภายในสตริงของการเกิดครั้งแรกของอักขระที่ระบุนี้โดยเริ่มการค้นหาที่ดัชนีที่ระบุ

ดังนั้นlastindexค่าของคุณจึงเป็น 0 เสมอและพบว่าสวัสดีในสตริงเสมอ


2

คำตอบที่ให้ว่าถูกต้องไม่ดีสำหรับการนับสิ่งต่างๆเช่นผลตอบแทนบรรทัดและเป็นคำที่ละเอียดเกินไป คำตอบในภายหลังดีกว่า แต่ทั้งหมดสามารถทำได้ง่ายๆด้วย

str.split(findStr).length

ไม่ทิ้งการจับคู่ต่อท้ายโดยใช้ตัวอย่างในคำถาม


1
สิ่งนี้ถูกกล่าวถึงในคำตอบอื่นแล้ว และคำตอบนั้นก็ทำได้ดีขึ้นเช่นกัน
michaelb958 - GoFundMonica

1
นี่ควรเป็นความคิดเห็นเกี่ยวกับคำตอบในคำถามไม่ใช่คำตอบอื่น
james.garriss

2

คุณสามารถจำนวนเหตุการณ์โดยใช้ฟังก์ชันไลบรารี inbuilt:

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

1
ไม่ได้ผลคุณควรระบุการอ้างอิงที่คุณใช้
Saikat

1

ลองเพิ่มlastIndex+=findStr.length()ที่จุดสิ้นสุดของลูปมิฉะนั้นคุณจะวนซ้ำไม่รู้จบเพราะเมื่อคุณพบสตริงย่อยคุณจะพยายามค้นหาซ้ำแล้วซ้ำอีกจากตำแหน่งสุดท้ายเดียวกัน


1

ลองอันนี้. จะแทนที่การจับคู่ทั้งหมดด้วยไฟล์-.

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

และหากคุณไม่ต้องการทำลายstrคุณสามารถสร้างสตริงใหม่ที่มีเนื้อหาเดียวกันได้:

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

หลังจากดำเนินการบล็อกนี้สิ่งเหล่านี้จะเป็นค่าของคุณ:

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


1

จากคำตอบที่มีอยู่ฉันต้องการเพิ่มเวอร์ชันที่ "สั้นกว่า" โดยไม่มี 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

สิ่งนี้จะพิจารณาว่าสตริงซ้ำหรือไม่เช่นหากคุณกำลังมองหาสตริง "xx" ในสตริง "xxx"
tCoe

1

นี่คือเวอร์ชันขั้นสูงสำหรับการนับจำนวนครั้งที่โทเค็นเกิดขึ้นในสตริงที่ผู้ใช้ป้อน:

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

วิธีการด้านล่างนี้แสดงจำนวนครั้งที่สตริงย่อยซ้ำกับสตริงทั้งหมดของคุณ หวังว่าจะใช้ได้เต็มที่กับคุณ: -

    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

นี่คือวิธีแก้ปัญหาอื่น ๆ โดยไม่ต้องใช้ regexp / รูปแบบ / matchers หรือแม้แต่ไม่ใช้ 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

หากคุณต้องการดัชนีของแต่ละสตริงย่อยภายในสตริงดั้งเดิมคุณสามารถทำอะไรบางอย่างกับ indexOf ดังนี้:

 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;

}


คำถามนี้มีอายุ 8 ปีและไม่มีข้อบ่งชี้ว่าเหตุใดจึงเป็นทางออกที่ดีกว่าวิธีแก้ปัญหาอื่น ๆ อีก 22 รายการที่โพสต์ไว้ก็น่าจะถูกลบออก
Jason Wheeler

0

โซลูชันนี้จะพิมพ์จำนวนการเกิดทั้งหมดของสตริงย่อยที่กำหนดตลอดทั้งสตริงรวมถึงกรณีที่มีการจับคู่ที่ทับซ้อนกัน

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);
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.