繁体   English   中英

从C中的字符串中删除最常见的单词

[英]Remove most common word from string in C

我需要从 C 中的字符串中删除所有出现的最常见单词。

如果文本中有多个单词重复相同的次数,则该函数应删除最接近字符串开头的最常见单词之一。 省略单词时,不应省略周围的空格和其他字符。 如果接收到的字符串不包含任何单词,则函数不需要做任何事情。

单词被定义为大写和小写字母的数组。 该函数不需要区分大小写字母

我的算法如下:

  • 找出最常见的单词出现在字符串中的次数
  • 然后逐字逐句通过字符串
  • 检查单词出现是否等于最常见单词的出现
  • 删除找到的最常用词

代码:

#include <stdio.h>
#include <limits.h>
#include <ctype.h>

int number_of_word_occurrence(char *s, char *start, char *end) {
  int number = 0;
  while (*s != '\0') {
    char *p = start;
    char *q = s;
    while (p != end) {
      if (*p != *q)break;
      p++;
      q++;
    }
    if (p == end)number++;
    s++;
  }
  return number;
}

int length(char *s) {
  char *p = s; int number = 0;
  while (*p != '\0') {
    p++;
    number++;
  }
  return number;
}

char *remove_most_common(char *s) {
  int n, max = INT_MIN;
  char *p = s;
  // Find max occurrence
  while (*p != '\0') {
    char *start = p;
    int word_found = 0;
    while (toupper(*p) >= 'A' && toupper(*p) <= 'Z' && *p != '\0') {
      word_found = 1;
      p++;
    }
    if (word_found) {
      n = number_of_word_occurrence(s, start, p);
      if (n > max)max = n;
    }
    p++;
  }
  p = s;
  int len = length(s);
  char *end = s + len;
  int i;
  // Removing most common word
  while (p != end) {
    char *start = p, *pp = p;
    int word_found = 0;
    while (toupper(*pp) >= 'A' && toupper(*pp) <= 'Z' && pp != end) {
      word_found = 1;
      pp++;
    }
    if (word_found) {
      n = number_of_word_occurrence(s, start, pp);
      // If word has max, then remove it
      if (n == max) {
        while (pp != end) {
          *start = *pp;
          start++;
          pp++;
        }
        end -= max; // resize end of string
        len-=max;
      }
    }
    p++;
  } 
  s[len+=2]='\0';
  return s;
}

int main() {
  char s[1000] = "Koristio sam auto-stop da dodjem do znaka stop ali prije stopa sam otvorio dekstop kompjutera stop";
  printf("%s ", remove_most_common(s) );
  return 0;
}
  • 应该删除的单词以粗体显示

示例 1:“Koristio sam auto- stop da dodjem do znaka stop ali prije stopa sam otvorio dekstop kompjutera stop

输出:“Koristio sam auto- da dodjem do znaka ali prije stopa sam otvorio dekstop kompjutera”

示例 2:“是字符串。”

输出:“是字符串。”

示例 3:“1 PsT 1 psT 2 3 Pst pstpst pst ”;

输出:““11 2 3 pstpst”

示例 4:“ oneeebigggwwooorrrddd ”;

输出: ””

你能帮我修复我的代码吗? 删除字符时出现一些错误。 另外,如果所有单词出现都相同,您能帮我删除最接近开头的单词吗?

  • 注意:解决任务时不允许使用string.hstdlib.h库中的函数以及 stdio.h 库中的sprintfsscanf函数。 不允许在函数中或全局创建辅助字符串。

所有主要问题都是由于字符串是信息,同时被积极更改。

一般来说,单词没有被正确标记。


以输入"hello world"为例,当在寻找要删除的时对字符串进行标记时, helloellolloloo中的每一个都被认为是 程序在扫描单词时仅将字符串前移一个字符。

程序应该将字符串推进当前标记的长度。


number_of_word_occurrence在进行比较时将任何子字符串视为有效单词

对于输入

Koristio sam auto- stop da dodjem do znaka stop ali prije stop a sam otvorio dek stop kompjutera stop

最大计数被错误地发现为5 ,用于stop 此问题与上述问题复合,并开始删除报告此发生计数的错误标记化数据。


概括地说,这种方法的一个大问题是,当您从字符串中删除一个单词时,该单词的出现次数将会不同,下次找到它时。 看着

hello hello hello world world

此处单词的最大出现次数为3 ,即hello 循环删除最大单词会第一次看到hello ,检查它的出现次数,发现它是3 ,最大值,并删除它。

 hello hello world world

对于第二个hello ,现在检查其出现次数将返回2 ,因为字符串已更改。 这不是3的最大值,因此字符串不变。


此外,字符串在更改时不会以空值结尾 - 仅在之后。 意思是搜索一个词可能会读取陈旧的数据,从而产生不好的结果。


对程序可以使用的功能的严格限制(特别是对动态内存和辅助缓冲区的限制)确实提供了非常详尽的解决方案。

一种解决方案是首先发现字符串中任何单词的最大出现count ,并持有指向该单词在字符串中的第一次出现的指针。 然后,执行count times 操作删除单词最后一次出现,这样您就可以始终将第一次出现作为比较点。

通过用字符串中紧随其后的所有内容(包括空终止字节)覆盖一个单词来删除它。

这是一个粗略的示例 - 很大程度上未经测试,但为所示示例提供了正确的结果。 使用-DDEBUG编译以查看更多信息。

#include <ctype.h>
#include <stdio.h>

typedef struct word {
    const char *base;
    size_t length;
} word;

#define length(s) (span((s), NULL))
size_t span(const char *base, int (*test)(int))
{
    const char *end = base;

    while (test ? test((unsigned char) *end) : *end)
        end++;

    return (size_t) (end - base);
}

int eql(word a, word b)
{
#ifdef DEBUG
    fprintf(stderr, "DEBUG: A{%zu}<<%.*s>> <=> B{%zu}<<%.*s>>\n",
            a.length, (int) a.length, a.base,
            b.length, (int) b.length, b.base);
#endif

    if (!a.length || !b.length || a.length != b.length)
        return 0;

    if (a.base == b.base)
        return 1;

    for (size_t i = 0; i < a.length; i++)
        if (tolower((unsigned char) a.base[i]) != tolower((unsigned char) b.base[i]))
            return 0;

    return 1;
}

word get_word(const char *s, const char **end)
{
    word w = { 0 };

    while (*s && !isalpha((unsigned char) *s))
        s++;

    w.base = s;
    w.length = span(s, isalpha);

    *end = (s + w.length);

    return w;
}


word find_last(const char *s, word mark, unsigned *count)
{
    word last = { 0 };
    unsigned c = 0;

    for (const char *end; *s; s = end) {
        word current = get_word(s, &end);

        if (eql(mark, current)) {
            last = current;
            c++;
        }
    }

    if (count)
        *count = c;

    return last;
}

word find_most_common(const char *s, unsigned *count)
{
    word most_common = { 0 };

    *count = 0;

    for (const char *end; *s; s = end) {
        word current = get_word(s, &end);

        if (eql(most_common, current))
            continue;

        unsigned c;

        (void) find_last(s, current, &c);

        if (c > *count) {
            most_common = current;
            *count = c;
        }
    }

    return most_common;
}

void copy(char *dest, char *source, size_t length)
{
    for (size_t i = 0; i < length; i++)
        dest[i] = source[i];
}

void remove_most_common(char *s)
{
    unsigned count = 0;
    word common = find_most_common(s, &count);

#ifdef DEBUG
    if (count)
        fprintf(stderr, "DEBUG: MOST COMMON WORD: [[%.*s]]x%u\n",
                (int) common.length, common.base, count);
#endif

    size_t len = length(s);

    while (count--) {
        word last = find_last(s, common, NULL);

        copy(
            (char *) last.base,
            (char *) last.base + last.length,
            len - (size_t) (last.base - s) + 1);

        len -= last.length;
    }
}

int main(void)
{
    char buffer[4096];

    if (!fgets(buffer, sizeof buffer, stdin))
        return 1;

    size_t len = length(buffer);

    if (len && buffer[len - 1] == '\n')
        buffer[len - 1] = 0;

    printf("<<%s>>\n", buffer);
    remove_most_common(buffer);
    printf("<<%s>>\n", buffer);
}

我决定编写一个新函数,从字符串中删除所有出现的单词,这完全符合您的要求。 您只需要提供出处和需要删除的单词即可。

代码:

#include <stdio.h>
#include <ctype.h> // toupper
#include <string.h> // strlen
#include <stdbool.h> // bool

void removeWord(char* source, char* removeThis)
{
    int i, j;
    bool wordFound;
    int sourceLength, removeLength;

    sourceLength = strlen(source);
    removeLength = strlen(removeThis);

    for(i = 0; i < sourceLength; ++i)
    {
        wordFound = true;

        for(j = 0; j < removeLength; ++j)
        {
            if(toupper(source[i + j]) != toupper(removeThis[j]))
            {
                wordFound = false;
                break;
            }
        }

        // It is not a word if the previous character or after the last one is alphabetic
        if(wordFound && (isalpha(source[i + j]) || (i > 0 && isalpha(source[i - 1]))))
        {
            wordFound = false;
        }

        if(wordFound)
        {
            for(j = i; j <= sourceLength - removeLength; ++j)
            {
                source[j] = source[j + removeLength];
            }

            --i;
            sourceLength -= removeLength;
        }
    }
}

int main()
{
    char string1[] = "Koristio sam auto-stop da dodjem do znaka stop ali prije stopa sam otvorio dekstop kompjutera stop";
    removeWord(string1, "stop");
    printf("\n%s", string1);

    char string2[] = {"This is string."};
    removeWord(string2, "this");
    printf("\n%s", string2);

    char string3[] = "1PsT1 psT2 3Pst pstpst pst";
    removeWord(string3, "pst");
    printf("\n%s", string3);

    char string4[] = "oneeebigggwwooorrrddd";
    removeWord(string4, "oneeebigggwwooorrrddd");
    printf("\n%s", string4);

    char string5[] = "Tomtom";
    removeWord(string5, "tom");
    printf("\n%s", string5);

    return 0;
}

输出:

Koristio sam auto- da dodjem do znaka  ali prije stopa sam otvorio dekstop kompjutera
 is string.
11 2 3 pstpst

Tomtom

基于此,您应该能够编写部分以找到最常用的单词,将其存储并将其提供给该函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM