【问题标题】:String char handling: How can I combine a post-processing loop into the primary loop? [closed]字符串字符处理:如何将后处理循环组合到主循环中? [关闭]
【发布时间】:2013-09-17 16:44:12
【问题描述】:

这是我试图解决但找不到更简单方法的问题:-

编写一个函数 squeez,删除 s1 中与字符串 s2 中任何字符匹配的每个字符。

到目前为止我所做的是以下代码:-

#include<stdio.h>
#include<string.h>
char *mysqueeze(char *a, char *b);
int main(void)
{
    int j = 0;
    char s1[100] = "krishna";
    char s2[100] = "kareem";
    char real[100];
    char *t = mysqueeze(s1,s2);
    for (int i = 0; i <= strlen(t); i++)  //1
        if (t[i] != '*')
    real[j++] = t[i];
    real[j] = '\0';        //4

    printf("%s\n", real);
}

char *mysqueeze(char *a, char *b)
{
    int i = 0, k;
    do
    {
        k = 0;
        while (b[k] != '\0')
        {
            if (a[i] == b[k]) {
                a[i] = '*';
                break;
            }
            k++;
        }
        i++;
    }
    while (a[i]);
    return a;
}

如何改进此代码?如何将 1 到 4 移至代码的循环部分?

【问题讨论】:

  • 使用与“p”不同的标记。也许是像'*'这样的符号。
  • 看看标准库函数strspn()和strcspn()。
  • 保留两个索引,一个用于您正在查看的角色,一个用于如果您正在查看的角色不在拒绝列表中,您将复制到的位置。最初,这两个索引将相同,为零。对于字符串中的每个字符,如果它不在拒绝列表中,则将其复制到当前复制到位置并增加复制到索引。增加查看索引。重复直到 EOS。将空终止符添加到复制到位置。
  • 1.正确拼写您的函数名称; 2、不要在循环条件中使用strlen(); 3. 不要使用像ab 这样的变量名,除非作为通用循环计数器; 4.不要比较有符号和无符号整数; 5. 正确缩进你的代码,即使是一行循环或条件语句也要使用大括号; 6.从main()正确返回,即使技术上没有要求; 7. 让一个函数完成整个工作,而不是让它完成一半工作,以至于调用者必须在它返回后完成工作。
  • 8.使用-Wall -Werror编译,9.使用strchr:这个函数可以写成四行代码。

标签: c string loops


【解决方案1】:

我希望这不是在回答家庭作业问题,但即使是这样,我想既然你已经有了一个可行的解决方案,我在技术上不会为他们做任何人的家庭作业。

我下面的两个解决方案都实现了原地崩溃算法,如果适用于该问题,那肯定是最有效的方法。如果问题需要一个折叠到另一个缓冲区的解决方案,则所需的修改将是最小的。正如 Paul Griffiths 在他的解决方案中提到的,最有效的解决方案(对于大型干草堆)涉及预先计算针布尔值的查找表。我在下面的mysqueeze3() 中实现了这样的解决方案。请注意,如果要重复使用同一组针,最好将函数拆分为两个单独的函数,一个用于初始化针数组,然后mysqueeze 实现将获取指向针数组查找表的指针而不是指向字符串的指针。

在实施任何解决方案时,您都需要验证它是否正确处理重复的针字符。为了测试这一点,我将您的 haystack 字符串扩展为四个相同字母的序列。您原始帖子的解决方案正确处理了这种情况。我的两个解决方案也都能正确处理这种情况。

可能需要注意的是,虽然mysqueeze3()mysqueeze2() 效率更高,但前者不是通用的解决方案,而后者是。查找表解决方案取决于 char 的大小为 8 位(或更小);即,最多有 256 个针值。如果您系统上的 char 为 9 位或更大,则需要相应地修改该解决方案。如果您系统上的 char 是 16 位或更大(可能是 unicode!),那么查找表解决方案就变得不切实际了。

#include<stdio.h>
#include<string.h>
typedef enum {false=0, true=1} bool;
char *mysqueeze(char *a, char *b);
char *mysqueeze2(char *haystack, const char *needles);
char *mysqueeze3(char *haystack, const char *needles);
void mysqueeze_griffiths(const char * first, const char * second, char * result);

int main(void)
{
    int j = 0;
    char s1[100] = "kkkkrrrriiiisssshhhhnnnnaaaa";
    char s2[100] = "kareem";
    char real[100];
    char *t = mysqueeze(s1,s2);
    for (int i = 0; i <= strlen(t); i++)  //1
        if (t[i] != '*')
           real[j++] = t[i];
    real[j] = '\0';        //4

    printf("mysqueeze:           %s\n", real);

    char s3[100] = "kkkkrrrriiiisssshhhhnnnnaaaa";
    char s4[100] = "kareem";
    printf("mysqueeze2:          %s\n", mysqueeze2(s3,s4));

    char s5[100] = "kkkkrrrriiiisssshhhhnnnnaaaa";
    char s6[100] = "kareem";
    printf("mysqueeze3:          %s\n", mysqueeze3(s5,s6));

    char s7[100] = "kkkkrrrriiiisssshhhhnnnnaaaa";
    char s8[100] = "kareem";
    char result[100];
    mysqueeze_griffiths(s7,s8, result);
    printf("mysqueeze_griffiths: %s\n", result);

}

char *mysqueeze(char *a, char *b)
{
    int i = 0, k;
    do
    {
        k = 0;
        while (b[k] != '\0')
        {
            if (a[i] == b[k]) {
                a[i] = '*';
                break;
            }
            k++;
        }
        i++;
    }
    while (a[i]);
    return a;
}

void mysqueeze_griffiths(const char * first, const char * second, char * result) {
    while ( *first ) {
        bool found = false;
        while ( *second ) {
            if ( *first == *second++ ) {
                found = true;
                break;
            }
        }
        if ( !found ) {
            *result++ = *first;
        }
        ++first;
    }

    *result = '\0';
}

char *mysqueeze2(char *haystack, const char *needles)
{
    char *dest=haystack, *src=haystack;
    while (*src)
    {
        const char* n;
        for (n=needles; *n; ++n)
        {
            if (*n==*src)
            {
                ++src;
                break;
            }
        }
        if (!*n)
           *dest++ = *src++;
    }
    *dest = '\0';
    return haystack;
}

char *mysqueeze3(char *haystack, const char *needles)
{
    char *dest=haystack, *src=haystack;
    bool needleArray[256];
    memset(needleArray,0,sizeof(needleArray));
    for (const char* n=needles; *n; ++n)
        needleArray[(unsigned char)*n] = true;
    while (*src)
    {
        if (needleArray[(unsigned char)*src])
            ++src;
        else
           *dest++ = *src++;
    }
    *dest = '\0';
    return haystack;
}

【讨论】:

  • +1 你可以通过使用bool needleArray[1 &lt;&lt; CHAR_BIT];来概括出对char大小的担忧,它在limits.h中,也可以将它用于something。写得不错。
  • @WhozCraig - 感谢您的赞美。顺便说一句,我真的很想知道这个“Craig”是谁.... 谁 Craig?
  • @WhozCraig - 啊哈!我检查了您的 ideone 解决方案并看到了查找表索引的转换...我忘记了...在默认情况下签名 char 的系统上,我原来的 mysqueeze3() 会以 UB 方式失败。现在已修复....将演员表添加到(无符号字符)。至于通过使用1&lt;&lt;CHAR_BIT...增加的一般性。谢谢,我没有想到这一点。但即便如此,查找表解决方案根本不适合字符大于 12 位的系统,因为表太大了......除非所有可用的字符都是
  • 合适的在旁观者的眼中。即使是 16 位,您所说的是 64 KB 的静态分配,也非常合理。现在,一个类似于 24bits+ 的字符,好吧.. 现在我们进入了“哎哟”的领域。
  • @WhozCraig - 4KB 或 64KB 的堆栈分配并不是真正的问题。正如您所说,堆栈上的 64KB 并不是什么大问题(尤其是对于桌面系统)。真正的问题是,在为所有针设置标志之前,需要将整个查找表初始化为全零。这为小型干草堆增加了大量开销。对于大型干草堆,或者如果一遍又一遍地使用查找表(没有重新初始化它)(通过引用将查找表传递给挤压函数),则可以摊销该成本。如果每次都初始化查找表,则不能。
【解决方案2】:

要真正回答您的问题,将 1 到 4 移动到函数中的最明显方法是将 real 作为第三个参数传递。但是,将a 中的字符替换为'*',然后复制其他字符是不必要的步骤。像这样更好:

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

void mysqueeze(const char * first, const char * second, char * result);

int main(void) {
    char first[] = "krishna";
    char second[] = "kareem";
    char result[100];

    mysqueeze(first, second, result);

    printf("Result: %s\n", result);

    return EXIT_SUCCESS;
}

void mysqueeze(const char * first, const char * second, char * result) {
    while ( *first ) {
        const char * t_second = second;
        bool found = false;
        while ( *t_second ) {
            if ( *first == *t_second++ ) {
                found = true;
                break;
            }
        }
        if ( !found ) {
            *result++ = *first;
        }
        ++first;
    }

    *result = '\0';
}

有更省时的方法来实现这个算法,例如单次遍历第二个字符串并在数组中为每个存在的字符设置一个标志,因此与第一个字符串的每次比较都可以在单个常量时间查找中完成。

已编辑:添加变量 t_second 以响应 phonetagger 的评论。

【讨论】:

  • 此解决方案无法正确处理大海捞针中连续重复针字符的情况。
  • @phonetagger:呃,不知道我是怎么错过的,你是对的,我更新了答案。
猜你喜欢
  • 2012-04-03
  • 2015-06-02
  • 2021-03-02
  • 2018-02-27
  • 1970-01-01
  • 1970-01-01
  • 2020-07-02
  • 2021-05-27
  • 1970-01-01
相关资源
最近更新 更多