查找子字符串,但不查找所有输入?

时间:2019-10-15 09:18:15

标签: c arrays string substring

我写了一个代码来查找较大字符串中最大子字符串的索引。

ab的数量相等时,将找到一个子字符串。

例如,给出12bbbbabaababb应该给出2 9,因为第一个出现的子字符串从索引0开始到索引9结束。3 10也是答案,但是由于这不是第一个出现的子字符串,因此这不是答案。

我编写的代码是:

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

void substr(char str[], int n) {
    int sum = 0;
    int max = -1, start;

    for (int i = 0; i < n; i++) {
        if (str[i]=='a') {
            str[i] = 0;
        } else if(str[i]=='b') {
            str[i] = 1;
        }
    }

    // starting point i
    for (int i = 0; i < n - 1; i++) {
        sum = (str[i] == 0) ? -1 : 1;

        // all subarrays from i
        for (int j = i + 1; j < n; j++) {
            (str[j] == 0) ? (sum += -1) : (sum += 1);

            // sum == 0
            if (sum == 0 && max < j - i + 1 && n%2==0) {
                max = j - i + 1;
                start = i-1;
            } else if (sum == 0 && max < j - i + 1 && n%2!=0) {
                max = j - i + 1;
                start = i;
            }
        }
    }

    // no subarray
    if (max == -1) {
        printf("No such subarray\n");
    } else {
        printf("%d %d\n", start, (start + max - 1));
    }
}


/* driver code */
int main(int argc, char* v[]) {
    int n;              // stores the length of the input
    int i = 0;          // used as counter

    scanf("%d", &n);

    n += 1;         // deals with the /0 at the end of a str

    char str[n];    // stores the total

    /* adding new numbers */
    while(i < n) {
        char new;
        scanf("%c", &new);
        str[i] = new;
        ++i;
    }

    substr(str, n);

    return 0;
}

它适用于很多值,但不适用于第二个示例(如下所示)。它应该输出2 9,但给出3 10。这是有效的子字符串,但不是第一个...

示例输入和输出应为:

Input      Input           Input
5          12              5
baababb    bbbbabaababb    bbbbb
Output     Output          Output
0 5        2 9             No such subarray

1 个答案:

答案 0 :(得分:1)

您遇到了一些问题,其中许多问题与数组大小和索引有关。

  • 当您读取数组时,您需要n个字符。然后,您将n的值增加以适应空终止符。对字符串进行空终止是一个好主意,但是末尾的'\0'实际上不是字符串数据的一部分。而是在创建数组时调整数组大小,并明确放置空终止符:

    char str[n + 1];
    
    // scan n characters
    str[n] = '\0';
    
  • 在C语言(和其他语言)中,范围由包含性下限定义,但由排除性上界定义:[lo, hi)。上限hi不在范围内,范围内有hi - lo个元素。 (带有n元素的数组是一种特殊情况,其中有效范围为[0, n)。)您应该拥护而不是反对这种约定。如果您的输出应该不同,请修改输出,而不是程序中的表示。

    (这是您的第一个示例,假设您有五个字符的字符串,实际上是如何读取并认为b位于第六位的。这是一个明显的错误。)

  • 最大有效子字符串的位置不取决于整个字符串长度是奇数还是偶数!

  • 不需要将所有“ a”和“ b”都转换为0和1的第一遍,它会破坏原始字符串。这不是什么大问题,但请记住这一点。

实际的问题是如何尝试查找子字符串。您的想法是将“ a”加1,然后将“ b”减1,但这是正确的,但是您没有正确地保留总和。对于每个可能的起点i,您扫描字符串的其余部分并寻找零和。仅当您将每个i的总和重置为零时,这才起作用。

void substr(char str[], int n)
{
    int max = 0;
    int start = -1;

    for (int i = 0; i + max < n; i++) {
        int sum = 0;

        for (int j = i; j < n; j++) {
            sum += (str[j] == 'a') ? -1 : 1;

            if (sum == 0 && max < j - i) {
                max = j - i;
                start = i;
            }
        }
    }

    if (max == 0) {
        printf("No such subarray\n");
    } else {
        printf("%d %d\n", start, start + max);
    }
}

为什么要初始化max = 0而不是-1?因为首先添加+ 1 / −1,所以您的检查永远找不到max == 0的子字符串,但是存在优化的可能性:如果您已经找到了一个长子字符串,则无需查看“字符串的结尾”:循环条件i + max < n将缩短搜索的时间。

(还有另一个原因:通常,大小和索引由无符号类型表示,例如size_t。如果将0用作初始值,则代码将适用于无符号类型。)

对于大型数组,该算法并不是最有效的方法,但是它应该可以工作。

相关问题