【问题标题】:Occurrences of substring in a string字符串中子字符串的出现次数
【发布时间】:2010-10-20 13:06:19
【问题描述】:

为什么以下算法对我没有停止? (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);

【问题讨论】:

  • 我们在 Udacity 中做得非常好:我们使用了 newSTR = str.replace(findStr, "");并返回 count = ((str.length() - newSTR.length())/findStr.length());
  • 字符的类似问题:stackoverflow.com/q/275944/873282
  • 难道你不想考虑搜索字符串的前缀是它的后缀的情况吗?在那种情况下,我认为任何建议的答案都不起作用。 here 就是一个例子。在这种情况下,您将需要更精细的算法,例如 CLRS 书中编码的 Knuth Morris Pratt(KMP)
  • 它不会为您停止,因为在达到“停止”条件(lastIndex == -1)后,您通过增加 lastIndex 的值来重置它(lastIndex += findStr.length();)
  • @Sid 如果你想要这种行为,你可以每次只将 lastIndex 增加 1 而不是 findStr.length。以我为例,我只需要知道一个字符是否匹配,不介意计算多个重叠。所以只取决于每个单独的用例

标签: java string


【解决方案1】:

每当您寻找下一次出现时,增加 lastIndex

否则它总是找到第一个子字符串(在位置 0)。

【讨论】:

    【解决方案2】:
    public int indexOf(int ch,
                       int fromIndex)
    

    返回此字符串中第一次出现指定字符的索引,从指定索引处开始搜索。

    所以你的lastindex 值总是0,它总是在字符串中找到hello

    【讨论】:

    • lastIndex 设置为返回值然后递增,循环迭代后它为 0 的唯一方法是子字符串的长度为 1
    【解决方案3】:

    尝试将lastIndex+=findStr.length() 添加到循环的末尾,否则您将陷入无限循环,因为一旦找到子字符串,您就会尝试从相同的最后位置一次又一次地找到它。

    【讨论】:

      【解决方案4】:
      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;希望对你有帮助

      【讨论】:

      • 代码包含错误。如果我们搜索单个字符,findStr.length() - 1 返回 0,我们处于无限循环中。
      【解决方案5】:

      你真的必须自己处理匹配吗?特别是如果你只需要出现次数,正则表达式会更整洁:

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

      【讨论】:

      • 这不会找到特殊字符,它会为下面的字符串找到 0 个计数:String str = "hel+loslkhel+lodjladfjhel+lo"; Pattern p = Pattern.compile("hel+lo");
      • 是的,如果你正确地表达你的正则表达式,它会的。尝试使用Pattern.compile("hel\\+lo"); + 符号在正则表达式中有特殊含义,需要转义。
      • 如果您正在寻找一个任意字符串并将其用作完全匹配而忽略所有特殊正则表达式字符,Pattern.quote(str) 是您的朋友!
      • 当 str = "aaaaaa" 时,这不适用于 "aaa"。有 4 个答案,但你给了 2 个
      • 此解决方案不适用于这种情况:str = "This is a test \\n\\r string", subStr = "\\r", 它显示 0 次出现。
      【解决方案6】:

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

      【讨论】:

      • 这对于字符串“aaa”和子字符串“aa”将失败。当计数为 2 时,它将返回 1。出现的索引是 [0,1]
      【解决方案7】:

      最后一行制造了一个问题。 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);
      

      【讨论】:

      • 这个回复是我一小时前发的帖子的精确副本;)
      • 请注意,这可能会或可能不会返回预期的结果。使用子字符串“aa”和搜索“aaa”的字符串,预期的出现次数可能是一个(由此代码返回),但也可能是两个(在这种情况下,您需要“lastIndex++”而不是“lastIndex += findStr.length()") 取决于您要查找的内容。
      • @olivier 没有看到... :( @stan 那绝对正确...我只是在修复问题中的代码...猜想这取决于 bobcom 的出现次数意味着什么字符串...
      • 人们什么时候才能学会用复制粘贴静态方法包装这样的东西?请参阅下面的答案,它也更加优化。
      • 这里的寓意是,如果您打算写一个答案,请首先检查其他人是否已经写了完全相同的答案。无论您的答案是被复制的还是独立编写的,让相同的答案出现两次确实没有任何好处。
      【解决方案8】:

      一个较短的版本。 ;)

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

      【讨论】:

      • return haystack.split(Pattern.quote(needle), -1).length - 1; 例如needle=":)"
      • @lOranger 如果没有,-1,它将丢弃尾随匹配项。
      • 哎呀,谢谢,很高兴知道!这将教我阅读 javadoc 中的小行...
      • 不错!但它只包括不重叠的匹配,不是吗?例如。在“aaa”中匹配“aa”将返回 1,而不是 2?当然,包括重叠或非重叠匹配都是有效的,并且取决于用户要求(可能是指示计数重叠的标志,是/否)?
      • -1 .. 尝试在 "aaaa" 和 "aa" 上运行它.. 正确答案是 3 而不是 2。
      【解决方案9】:

      使用来自 Apache Commons Lang 的 StringUtils.countMatches 怎么样?

      String str = "helloslkhellodjladfjhello";
      String findStr = "hello";
      
      System.out.println(StringUtils.countMatches(str, findStr));
      

      输出:

      3
      

      【讨论】:

      • 无论这个建议多么正确,它都不能被接受为解决方案,因为它没有回答 OP 的问题
      • 这是弃用还是什么.. 我的 IDE 无法识别
      • @VamsiPavanMahesh StringUtils 是一个 Apache Commons 库。在这里查看:commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
      • 这个答案是彼得劳里一天前的答案的副本(见下文)。
      • StringUtils 没有countMatches 方法。
      【解决方案10】:

      试试这个。它用- 替换所有匹配项。

      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
      

      【讨论】:

        【解决方案11】:

        这是用于计算令牌在用户输入的字符串中出现的次数的高级版本:

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

        【讨论】:

          【解决方案12】:

          下面的方法显示了子字符串在整个字符串上重复了多少次。希望对你充分利用:-

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

          【讨论】:

            【解决方案13】:

            给出的正确答案对于计算行返回之类的东西没有好处,而且过于冗长。稍后的答案会更好,但所有这些都可以通过

            来实现
            str.split(findStr).length
            

            它不会使用问题中的示例删除尾随匹配项。

            【讨论】:

            • 这已经被another answer 覆盖了;这个答案也做得更好。
            • 这应该是对相关答案的评论,而不是另一个答案。
            【解决方案14】:

            这是另一个不使用 regexp/patterns/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);
            

            【讨论】:

              【解决方案15】:

              如果您需要原始字符串中每个子字符串的索引,您可以使用 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;
              }
              

              【讨论】:

                【解决方案16】:

                正如@Mr_and_Mrs_D 建议的那样:

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

                【讨论】:

                  【解决方案17】:

                  就是这样,封装在一个很好的可重用方法中:

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

                  【讨论】:

                    【解决方案18】:

                    很多给定的答案在以下一项或多项上都失败了:

                    • 任意长度的模式
                    • 重叠匹配(例如在“23232”中计算“232”或在“aaa”中计算“aa”)
                    • 正则表达式元字符

                    这是我写的:

                    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 中没有这样的东西。
                    • 处理重叠模式的最佳答案。
                    【解决方案19】:

                    根据现有答案,我想添加一个不带 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
                    

                    【讨论】:

                    • 这个会考虑到字符串是否重复,例如,如果您在字符串 'xxx' 中查找字符串 'xx'。
                    【解决方案20】:

                    您可以使用内置库函数出现次数:

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

                    【讨论】:

                    • 不起作用,你应该指定你使用的依赖。
                    【解决方案21】:

                    我很惊讶没有人提到这个班轮。简单、简洁,性能略优于str.split(target, -1).length-1

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

                    【讨论】:

                    • 应该是最佳答案。谢谢!
                    • 完美答案!!
                    • 仅供参考:这会引发 ArithmeticException,因为在空目标字符串的情况下除以零。
                    • @Attila 当然可以......就像如果 str 或 target 为空,它会抛出 NullPointerException 。计算空字符串是什么意思?
                    【解决方案22】:
                    public int countOfOccurrences(String str, String subStr) {
                      return (str.length() - str.replaceAll(Pattern.quote(subStr), "").length()) / subStr.length();
                    }
                    

                    【讨论】:

                    • 好答案。你能介意添加一些关于它是如何工作的注释吗?
                    • 当然,str - 是我们的源字符串,subStr - 是一个子字符串。目标是计算 str 中 subStr 的出现次数。为此,我们使用公式:(ab)/c,其中 a - str 的长度,b - 没有出现所有 subStr 的 str 的长度(为此,我们从 str 中删除所有出现的 subStr),c - subStr 的长度.所以,基本上我们从 str 的长度中提取 - str 的长度没有所有 subStr,然后我们将结果除以 subStr 的长度。如果您有任何其他问题,请告诉我。
                    • 桑托什,不客气!重要的部分是对 subStr 使用 Pattern.quote,否则在某些情况下可能会失败,例如:str = "This is a test \\n\\r string", subStr = "\\r"。此处提供的一些类似答案不使用 Pattern,因此在这种情况下它们会失败。
                    • 没有理由使用正则表达式,使用replace,而不是replaceAll
                    【解决方案23】:
                    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 个解决方案更好的解决方案,它可能应该被删除
                    【解决方案24】:

                    此解决方案打印整个字符串中给定子字符串的出现总数,还包括确实存在重叠匹配的情况。

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

                    【讨论】:

                      【解决方案25】:

                      ? 只是一个桃色的答案

                          public int countOccurrences(String str, String sub) {
                              if (str == null || str.length() == 0 || sub == null || sub.length() == 0) return 0;
                              int count = 0;
                              int i = 0;
                              while ((i = str.indexOf(sub, i)) != -1) {
                                  count++;
                                  i += sub.length();
                              }
                              return count;
                          }
                      

                      【讨论】:

                        【解决方案26】:

                        我刚才在一次采访中被问到这个问题,我完全空白。 (像往常一样,我告诉自己,面试结束的那一刻我会得到解决方案)我做了,电话结束后 5 分钟 :(

                            int subCounter=0;
                            int count =0;
                            for(int i=0; i<str.length(); i++) {
                                if((subCounter==0 && "a".equals(str.substring(i,i+1))) 
                                        || (subCounter==1 && "b".equals(str.substring(i,i+1)))
                                        || (subCounter==2 && "c".equals(str.substring(i,i+1)))) {
                                    ++subCounter;
                                }
                                if(subCounter==3) {
                                    count = count+1;
                                    subCounter=0;
                                }
                            }
                            System.out.println(count);
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2018-04-02
                          • 2020-02-21
                          • 2012-02-12
                          • 2017-04-03
                          相关资源
                          最近更新 更多