【问题标题】:How to find all occurrences and all position of a substring in a string?如何查找字符串中子字符串的所有出现和所有位置?
【发布时间】:2017-11-20 23:49:42
【问题描述】:

我需要查找所有出现的位置并输出字符串中子字符串的所有位置。

例如:我的字符串是abaaab,我的子字符串是aa,位置是3和4,因为在aaa中我的子字符串重复了两次。

我希望从右到左打印末尾的位置,在子字符串的位置之后我想要我的子字符串的出现次数。

我试过了,我有这个:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    char *str, c;
    int x = 0, y = 1;

    str = (char*)malloc(sizeof(char));

    printf("Inserisci stringa principale : ");

        while (c != '\n') {
        // read the input from keyboard standard input
        c = getc(stdin);

        // re-allocate (resize) memory for character read to be stored
        str = (char*)realloc(str, y * sizeof(char));

        // store read character by making pointer point to c
        str[x] = c;

        x++;
        y++;
        }

    str[x] = '\0'; // at the end append null character to mark end of string

    printf("\nLa stringa inserita : %s", str);

      char *sub, b;
      int w = 0, z = 1;

      sub = (char*)malloc(sizeof(char));

      printf("Immetti sottostringa da cercare : ");

          while (b != '\n') {
            // read the input from keyboard standard input
            b = getc(stdin);

            // re-allocate (resize) memory for character read to be stored
            sub = (char*)realloc(sub, z * sizeof(char));

            // store read character by making pointer point to c
            sub[w] = b;

            w++;
            z++;
          }

      sub[w] = '\0'; // at the end append null character to mark end of string

    char *p1, *p2, *p3;
    int i=0,j=0,flag=0;

      p1 = str;
      p2 = sub;

      for(i = 0; i<strlen(str); i++)
      {
        if(*p1 == *p2)
          {
              p3 = p1;
              for(j = 0;j<strlen(sub);j++)
              {
                if(*p3 == *p2)
                {
                  p3++;p2++;
                } 
                else
                  break;
              }
              p2 = sub;
              if(j == strlen(sub))
              {
                 flag = 1;
                printf("\nSottostringa trovata all'indice : %d\n",i);
              }
          }
        p1++; 
      }
      if(flag==0)
      {
           printf("Sottostringa non trovata");
      }
    free(str);
    free(sub);
    return (0);
    }

但它只显示第一次出现的位置,而不是出现的次数。

【问题讨论】:

  • 一个简单的方法是在循环中使用strstr;或者是一个不允许你使用strstr的练习?

标签: c string char substring


【解决方案1】:

您的代码中存在多个问题:

  • 您的字符串重新分配方案不正确:分配的空间对于字符串来说太短了一个字节,并且您从未测试内存分配失败。如果您的系统支持,您可以使用getline(),或者至少编写一个函数来分解代码。

  • c 在您第一次循环测试 c != '\n' 时未初始化:这具有未定义的行为。

  • 您的匹配算法太复杂:您同时使用索引值和移动指针。使用其中一种。

这是一个简化版:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* read an allocated string from stream.
   stop at newline, not included in string.
   Return NULL upon EOF
 */
char *my_getline(FILE *stream) {
    char *line = NULL;
    size_t pos = 0;
    int c;

    while ((c = getc(stream)) != EOF) {
        char *newp = realloc(line, pos + 2);
        if (newp == NULL) {
            free(line);
            return NULL;
        }
        line = newp;
        if (c == '\n')
            break;
        line[pos++] = (char)c;
    }
    if (line) {
        line[pos] = '\0';
    }
    return line;
}

int main(void) {
    char *str, *sub;
    size_t len1, len2, i, count = 0;

    // type the main string
    printf("Inserisci stringa principale :\n");
    str = my_getline(stdin);

    // type the substring to search for
    printf("Immetti sottostringa da cercare :\n");
    sub = my_getline(stdin);

    if (str && sub) {
        len1 = strlen(str);
        len2 = strlen(sub);
        for (i = 0; i + len2 <= len1; i++) {
            if (!memcmp(str + i, sub, len2)) {
                count++;
                // substring found at offset
                printf("Sottostringa trovata all'indice : %zu\n", i);
            }
        }
        if (count == 0) {
            // substring not found
            printf("Sottostringa non trovata\n");
        }
    }
    free(str);
    free(sub);
    return 0;
}

注意事项:

  • 上面的代码在搜索字符串的每个偏移处查找空子字符串的匹配项。是否应该找到匹配是规范的问题,但这种行为与strstr()的行为一致。

  • 您也可以使用标准函数strstr() 来定位匹配项。

这是使用strstr()的主循环版本:

if (str && sub) {
    for (char *p = str; (p = strstr(p, sub)) != NULL; p++) {
        count++;
        // substring found at offset
        printf("Sottostringa trovata all'indice : %tu\n", p - str);
        if (*p == '\0')  /* special case for the empty string */
            break;
    }
    if (count == 0) {
        // substring not found
        printf("Sottostringa non trovata\n");
    }
}

【讨论】:

    【解决方案2】:

    我检查了你的代码,你的代码似乎有问题

    if(j == strlen(sub))
    

    由于 j 从 0 开始,它总是比子字符串的长度小 1,请将代码更改为

    if(j+1 == strlen(sub))
    

    它应该可以解决您的问题。

    对于出现次数,每当与子字符串匹配时,您需要另一个变量来计算,修改 if 块

    if(j+1 == strlen(sub))
    {
          flag = 1;
          occurrences+=1;  //declare variable occurrences and initialize it to 0
          printf("\nSottostringa trovata all'indice : %d\n",i);
    }
    

    然后在循环结束后,只需打印“发生”即可获得所需的结果。

    另外这也不是解决问题的有效方法,可以参考

    https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-string-searching-algorithms/

    为了更好的方法。

    【讨论】:

    • 如何先显示正确的位置?例如,如果字符串是 aaaooaaoodf 并且子字符串是 aa 而不是 0 1 5 得到 5 1 0?
    • 将索引压入堆栈,循环结束后一个一个出栈。
    • 我没明白你的意思,我作为输出我需要先获取变量出现,但是如果每次都打印循环中的模具,并且如果模具输出作为最后一个打印事情,然后是位置,但从最后开始,用我的代码我该怎么做?
    • 好吧,更简单的方法是执行反向 for 循环,在您的场景中将外部循环更改为 for(i = strlen(l)-1; i>=0; i--) 和而不是 p1++ 执行 p1--,以及在循环开始之前将 p1 指向数组的最后一个索引,保持其余代码相同,您将得到结果。
    • 我之前的建议是使用堆栈数据结构,以防你不了解堆栈参考hackerearth.com/practice/data-structures/stacks/…
    【解决方案3】:

    查找每个事件的简单方法是在循环中调用strstr。每次匹配后,让strstr 在找到匹配的位置之后搜索一个位置:

    int main( ) {
    
        const char *string = "abaaab";
        const char *toSearch = "aa";
        int nrOfOccurences = 0;
        printf("searching for occurences of '%s' in string '%s':\n", string, toSearch);
        const char* pos = string;
        while (pos) {
            pos = strstr(pos, toSearch);
            if (pos) {
                printf("found occurence at position %td\n", pos-string);
                nrOfOccurences++;
                pos++;  // skip one character
            }
        }
        nrOfOccurences = findRecursive(string, toSearch, 0,0);
        printf("nr of occurences: %d\n", nrOfOccurences);
        return 0;
    }
    

    如果您需要 - 以某种方式说明 - 打印从最后一个开始的事件,您可以使用如下递归函数。上面代码中的注释说明了如何使用它:

    int findRecursive(const char* str, const char* toSearch, ptrdiff_t pos, int nrOfOccurences) {
    
        char *next = strstr(str, toSearch);
        if (next) {
            ptrdiff_t foundPos = pos + next - str;
            nrOfOccurences = findRecursive(next+1, toSearch, foundPos+1, nrOfOccurences+1);
            printf("occurence found at position %td\n", foundPos);
        }
        return nrOfOccurences;
    }
    

    【讨论】:

    • pos-string 的类型为 ptrdiff_t,因此格式说明符应为 "%td"
    • @Govind Parmar:好的,谢谢。相应地调整了答案。
    猜你喜欢
    • 2011-05-01
    • 1970-01-01
    • 2013-03-22
    • 2013-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多