【问题标题】:How to separate a string into an array of the unique characters/ strings如何将字符串分成唯一字符/字符串的数组
【发布时间】:2015-10-18 17:28:10
【问题描述】:

基本上我想知道是否有可能(如果可以的话)从左到右读取一个字符串,并在找到新字符串后贪婪地终止并追加。例如。

"ABCABCABCABC" 将给出 {"A" "B" "C" "AB" "CA" "BC" "ABC"}

我一整天都在尝试,最终得到的只是代码损坏和程序崩溃。

这就是我所拥有的不起作用。数组定义为 *a[linelen]

for(i =0; i < linelen ;i++)
{
    j=0;
    k=0; 
    tempstr[j] = input[i]; // move character from input to tempstring 
        for(k=0; k< array_size; k++) //search through array
        {
            tempstr[j] = input[i];
            if(*a != tempstr)//(strcmp(a,tempstr)) != 0) // if str not in array
            {
                printf("%s\n", a[0]); //debug
                a[array_size] = tempstr;
                //strcpy(a[array_size], tempstr); //copy str into array
                array_size++;
                memset(tempstr,0,linelen-i); // reset tempstr to empty
                j=0;

            } 
            if( *a == tempstr)//(strcmp(a[array_size],tempstr)) == 0)
            {
                j++;
                tempstr[j] = input[i+1];
                if(i != linelen -1) // otherwise if tempstr already in array
                {
                    printf("%s\n",a[0]); //debug
                    j++;
                    tempstr[j] = input[i+1];
                }
                else if (i == linelen -1) // if it is the last letter
                {
                    a[array_size] = tempstr;
                    //strcpy(a[array_size], tempstr); // add to array
                    break;
                }

            }
        }

}

【问题讨论】:

  • 可以吗?当然,很多strstr() 最终应该这样做。可以有效地完成吗?不知道。
  • 我不确定你为什么要 BC 和 ABC 而不是 BCA?
  • 欢迎来到 Stack Overflow。请尽快阅读About 页面。您应该向我们展示您认为最能处理此问题的代码,并展示它在崩溃之前产生的结果(您至少有一些诊断输出,不是吗?),以便我们可以看到您所看到的。我们会很乐意帮助您解决代码中的问题。我们不会简单地为您编写程序。
  • @maxime:当代码读取A时,它是新的; B是新的; C是新的。然后它读取一个新的 A 但那不是新的,所以它也读取 B,因为 AB 是新的;然后读取CA;然后 BC 是新的,最后 AB 不是新的,但 ABC 是。
  • 请查看如何创建 MCVE (How to create a Minimal, Complete, and Verifiable Example?)。这就是我们应该得到的工作。请注意,想要查看您的代码的一个原因是它让我们有机会了解您的编码水平,因此我们可以避免提出超出您理解水平的解决方案。您将需要使用一些字符串比较功能;简单地比较指针是行不通的。 OTOH,strcmp() 需要以空字符结尾的字符串。我认为您的代码不是以空值结尾的字符串。

标签: c algorithm


【解决方案1】:

这是一个使用简单的字符数组来存储“seen”字符串的例子:

#include <stdio.h>

#if 0
#define dbg(_fmt...)        printf(_fmt)
#else
#define dbg(_fmt...)        /**/
#endif

// NOTE: could be char * and realloc if necessary
char seen[5000];

// find -- find old string
// RETURNS: 1=found, 0=no match
int
find(char *str)
{
    char *lhs;
    char *rhs;
    int foundflg;

    dbg("find: str='%s'\n",str);

    rhs = str;
    lhs = seen;
    dbg("find: lhs='%s'\n",seen);

    foundflg = 0;
    for (;  lhs < str;  ++lhs, ++rhs) {
        dbg("find: TRY lhs='%s' rhs='%s'\n",lhs,rhs);

        if (*lhs != *rhs) {
            dbg("find: SKIP\n");
            for (;  *lhs != 0;  ++lhs);
            rhs = str - 1;
            continue;
        }

        if ((*lhs == 0) && (*rhs == 0)) {
            dbg("find: MATCH\n");
            foundflg = 1;
            break;
        }

        if (*rhs == 0)
            break;
    }

    return foundflg;
}

void
sepstr(const char *inp)
{
    int chr;
    char *lhs;
    char *rhs;
    int finflg;

    lhs = seen;
    rhs = seen;
    finflg = 0;

    for (chr = *inp;  chr != 0;  chr = *++inp) {
        *rhs++ = chr;
        *rhs = 0;

        if (find(lhs)) {
            finflg = 1;
            continue;
        }

        printf("%s\n",lhs);
        lhs = ++rhs;
        finflg = 0;
    }

    if (finflg)
        printf("%s\n",lhs);
}

int
main(int argc,char **argv)
{

#if 1
    sepstr("ABCABCABCABC");
#else
    sepstr("ABCABCABCABCABC");
#endif
}

这是第二种方法:

#include <stdio.h>

char out[500];

#ifdef BIG
#define SEEN 256
#else
#define SEEN (26 + 1)
#endif

char seen[SEEN][SEEN];

void
sepstr(const char *inp)
{
    int chr;
    char *prv;
    char *rhs;

    prv = seen[0];

    rhs = out;
    for (chr = *inp;  chr != 0;  chr = *++inp) {
        *rhs++ = chr;

#ifndef BIG
        chr = (chr - 'A') + 1;
#endif

        if (prv[chr]) {
            prv = seen[chr];
            continue;
        }

        *rhs = 0;
        printf("%s\n",out);

        prv[chr] = 1;
        rhs = out;
        prv = seen[0];
    }

    if (rhs > out) {
        *rhs = 0;
        printf("%s\n",out);
    }
}

int
main(void)
{

#if 1
    sepstr("ABCABCABCABC");
#else
    sepstr("ABCABCABCABCABC");
#endif

    return 0;
}

这里是每个人的程序的一些基准(时间在 ns 和 printf nop'ed):

 第一最小作者
         527 137 craig1 -- 原始 -- 使用单个可见字符数组
         146 39 craig2 -- 修改 -- 使用 2D 可见表
       45234 45234 felix1 -- original -- 只能执行一次
       40460 656 felix2 -- 使用固定输入
          24 18 machine1 -- original -- 在堆栈上使用缓冲区[20][20]
         908 417 machine2 -- 修改 -- 使用全局缓冲区[20][20]
       43089 1120 milevyo1 -- 原版
       42719 711 milevyo2 -- parseString tmp 是堆栈缓冲区,没有 malloc
        7957 429 milevyo3 -- NewNode 使用固定池无 malloc
        7457 380 milevyo4 -- 删除链表

【讨论】:

  • 是的,这很有趣,尽管这种方式不太具有可比性,因为并非所有解决方案都具有相同的灵活性。您将所有内容连续存储的方法可能非常适合缓存局部性。大量时间通常花在堆分配上。我想知道我的每个字符串长度都有“桶”的想法是否有助于缩放。
  • @FelixPalmen 添加了速度提高 4 倍的新方法。你是对的。缓存“热”很重要。在表中,左列是第一次(缓存冷),第二列是 N 次运行中的最佳(缓存热)。您的原始文件在标准输入上丢失了。我和其他人一起调整了它(例如 doit("ABCABCABCABC"))——主要加速。请注意,对于我来说,缓存冷和 -DBIG 很慢,但缓存热是一样的。你的“桶”似乎就像一个哈希。如果是这样, strlen + first char 会是更好的键吗? (例如 2D 查找表)或者,我错过了吗?
  • 使用循环来驱动测试(例如for/continue,或for( ... i+=step),甚至goto)是迄今为止处理许多迭代情况的最有效方法。好主意。
  • @DavidC.Rankin 谢谢。我在谷歌的 foobar 挑战“小黄人无聊游戏”中失败了。正确的方法是动态编程的形式,所以做了一些研究,并开始寻找用例:请参阅我在“吸血鬼挑战”stackoverflow.com/questions/32834843/… 中的答案。对于当前的问题,我只是有机地想到了第二个算法,但是,我认为它是“带制表的动态编程”[避免昂贵的列表/树搜索的技术] 并得到 O(n) vs O(n^2) 或更糟.
【解决方案2】:

是的,有可能。

您只需要跟踪不同的字符串出现。最简单的方法是使用 Set。

编辑:请参阅此处了解如何在 C 中实现 Set:How to implement a Set data structure

Edit2:你可以在这里找到一个实现(我没有测试过):HashSet.c

【讨论】:

  • 我想这是对这个问题的直接而正确的答案,但它并没有太大帮助。哪个标准 C 标头提供 Set 类型?
  • @JonathanLeffler 很容易重新实现一个简单的哈希集。你只需要一个链表数组和一个哈希函数。
  • @Maxime:我们需要先查看 OP 的代码,然后才能评估具有动态内存分配(甚至结构)的链表数组是否在他的知识范围内。我的直觉是,这样的建议会让他感到困惑,而不是帮助他——他的知识还没有达到那个水平。我希望我是错的,但是没有看到他们的代码,没有人可以确定。
  • 感谢您的回复,但我不知道集合是什么或如何实现。我做了一些快速的谷歌搜索,但我似乎无法理解如何轻松实现它
  • @E.Munch,我已经编辑了答案以添加更多信息。
【解决方案3】:

在这里,应该这样做:

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

int main(void)
{
    char str[] = "ABCABCABCABC";

    //length of str
    size_t len = strlen(str);

    //buffer to hold extracted strings
    char buffer[20][20];

    //i : 1st buffer index , j : 2nd buffer index , n : variable used in the loop
    size_t i = 1 , j = 0 , n = 0 ;

    //store str[0] and '\0' to form a string : buffer[0]
    buffer[0][0] = str[0];
    buffer[0][1] = '\0';

    //has the string been found ?
    bool found = false;

    //n should start by 1 since we stored str[0] int buffer already
    for( n = 1 ; n < len ; n++ )
    {
        //store str[n] in buffer , increment j , and store '\0' to make a string
        buffer[i][j] = str[n];
        j++;
        buffer[i][j] = '\0';

        //this loop check if the string stored is found in the entire buffer.
        for( int x = 0 ; x < i ; x++ )
        {
            if( strcmp(buffer[i],buffer[x]) == 0 )
            {
                found = true;
            }
        }

        //if the string has not been found,increment i,to make a new string in the next iteration
        if( found == false)
        {
            i++;
            j = 0;
        }
        //reset the bool value
        found = false;
    }

    //print the strings stored in buffer.
    for( int x = 0 ; x < i ; x++ )
    {
        printf("%s\n",buffer[x]);
    }
}

【讨论】:

  • 如果您有兴趣,我将每个人的程序的基准添加到我的答案底部。
【解决方案4】:

这是 C99 中的一个完全动态的解决方案,但它仍然是草稿代码(根本不检查内存不足的情况)并且可能效率很低(例如不使用散列):

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

/* a string builder */
typedef struct sb
{
    size_t capacity;
    size_t len;
    char str[];
} sb;

/* a container of substrings, represented by string builders */
typedef struct strcontainer
{
    size_t capacity;
    size_t len;
    sb **substrings;
} strcontainer;

/* global maximum length of substrings seen so far */
static size_t maxlen;

/* container instances */
static strcontainer *containers;

/* initialize a new container */
static void strcontainer_init(strcontainer *self)
{
    self->capacity = 16;
    self->len = 0;
    self->substrings = malloc(16 * sizeof(sb *));
}

/* create a new string builder */
static sb *sb_create(void)
{
    sb *self = malloc(sizeof(sb) + 16);
    self->capacity = 16;
    self->len = 0;
    self->str[0] = 0;
    return self;
}

/* append a character to a string builder */
static sb *sb_append(sb *self, int c)
{
    self->str[self->len++] = (char) c;
    if (self->len == self->capacity)
    {
        self->capacity *= 2;
        self = realloc(self, sizeof(sb) + self->capacity);
    }
    self->str[self->len] = 0;
    return self;
}

/* get plain C string from a string builder */
static const char *sb_str(const sb *self)
{
    return &(self->str[0]);
}

/* check whether a substring with the contents of the given string builder is
 * already present and increase maximum length and count of containers if
 * necessary */
static int sb_ispresent(const sb *self)
{
    if (self->len > maxlen)
    {
        size_t oldlen = maxlen + 1;
        maxlen = self->len;
        containers = realloc(containers,
                (maxlen + 1) * sizeof(strcontainer));
        for (; oldlen <= maxlen; ++oldlen)
        {
            strcontainer_init(containers + oldlen);
        }
        return 0;
    }

    strcontainer *container = containers + self->len;

    for (size_t i = 0; i < container->len; ++i)
    {
        if (!strcmp(sb_str(self), sb_str(container->substrings[i])))
        {
            return 1;
        }
    }
    return 0;
}

/* check whether container has space left and if not, expand it */
static void strcontainer_checkexpand(strcontainer *self)
{
    if (self->len == self->capacity)
    {
        self->capacity *= 2;
        self->substrings = realloc(self->substrings,
                self->capacity * sizeof(sb *));
    }
}

/* insert a string builder as new substring in a container */
static void strcontainer_insert(strcontainer *self, sb *str)
{
    strcontainer_checkexpand(self);
    self->substrings[self->len++] = str;
}

/* insert this string builder instance in the appropriate containers */
static void sb_insert(sb *self)
{
    strcontainer_insert(containers, self);
    strcontainer_insert(containers + self->len, self);
}

int main(void)
{
    int c;
    size_t i = 0;

    /* idea here: allocate a global container and one for each substring
     * length. start with a maximum length of 1, makes 2 containers */
    containers = malloc(2 * sizeof(strcontainer));
    strcontainer_init(containers);
    strcontainer_init(containers+1);
    maxlen = 1;

    /* string builder for the substring */
    sb *builder = 0;

    while ((c = getchar()) != EOF)
    {
        /* on newline, output what we have so far */
        if (c == '\n')
        {
            while (i < containers->len)
            {
                puts(sb_str(containers->substrings[i++]));
            }
            continue;
        }

        /* ignore carriage returns, maybe ignore some other characters
         * here too? */
        if (c == '\r') continue;

        /* append each character to the string builder */
        if (!builder) builder = sb_create();
        builder = sb_append(builder, c);

        /* check whether we have seen the string already after every append */
        if (!sb_ispresent(builder))
        {
            /*then insert and restart with a new string builder */
            sb_insert(builder);
            builder = 0;
        }
    }

    /* more output after EOF */
    while (i < containers->len)
    {
        puts(sb_str(containers->substrings[i++]));
    }

    /* if we still have a builder, there was some non-unique text left over
     * at the end of the input */
    if (builder)
    {
        fprintf(stderr, "Left over: `%s'\n", sb_str(builder));
    }

    /* might want to clean up on the heap with some free()s ...
     * not strictly necessary at end of program */

    return 0;
}

示例:

> echo "ABCABCABCABCABC" | ./greadystring
A
B
C
AB
CA
BC
ABC
Left over: `ABC'

【讨论】:

  • 如果您有兴趣,我将每个人的程序的基准添加到我的答案底部。
【解决方案5】:

我使用链表来避免重复项。检查最后的输出。

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


typedef struct NODE NODE;
struct NODE{
    char value[20];
    NODE *next;
};
NODE *head=NULL;
/*_________________________________________________
*/
NODE *FindNode(char *p){
    NODE *tmp=head;
    while(tmp){
        if(_stricmp(tmp->value,p)==0) break;
        tmp=tmp->next;
    }
    return tmp;
}
/*_________________________________________________
*/
NODE *NewNode(char *p){
    NODE *tmp=calloc(1,sizeof(NODE));
    if(tmp){
        strcpy(tmp->value,p);
    }
    return tmp;
}
/*_________________________________________________
*/

int AddNode(char *p){

    NODE * tmp=FindNode(p);
    if(!tmp){
        if((tmp=NewNode(p))){
            if(!head)
                head=tmp;
            else{
                NODE *_tmp=head;
                while(_tmp->next)_tmp=_tmp->next;
                _tmp->next=tmp;
            }
            return 1;
        }

    }
    return 0;
}
/*_________________________________________________
*/
void printNodes(void){
    NODE *tmp=head;
    printf("{");
    while(tmp){
        printf("\"%s\"",tmp->value);
       tmp=tmp->next;
        if(tmp)printf(",");
    }
    printf("}\n");
 }
/*_________________________________________________
*/
void deleteNodes(void){
    NODE *tmp=head;
    while(tmp){
       head=tmp->next;
        free(tmp);
        tmp=head;
    }
 }
/*_________________________________________________
*/
void parseString(char *buff){
    int  buffSize=  strlen(buff);
    if(!buffSize) return;

    char *tmp;
    char *ptr=buff;

    int j=1,n=0;

    for(ptr=buff;n<buffSize;ptr+=j){
        tmp=calloc(sizeof(char),j+1);
        strncpy(tmp,ptr,j);
        if(!*tmp){
            free(tmp);
            break;
        }

        if(!AddNode(tmp)){
            j++;
            ptr-=j;

        }else
            n+=j;
        free(tmp);
    }

    printf("%s\n",buff);
    printNodes();
    printf("\n");
    deleteNodes();

}
int main(void){
    parseString("ABCABCABCABC");
    parseString("ABCABCABCABCABCABCABCABC");
    return 0;
}

这是输出:

ABCABCABCABC
{"A","B","C","AB","CA","BC","ABC"}

ABCABCABCABCABCABCABCABC
{"A","B","C","AB","CA","BC","ABC","ABCA","BCAB","CABC"}

【讨论】:

  • 链表可能是最不有效的方法之一,但只要它产生正确的输出,它就是一个答案:)。尽管如此,代码上还是有一些 cmets: 1. _stricmp() 是什么?非标准且其余代码看起来符合标准,为什么要引入这个? 2. 避免在 C 中使用下划线开头的标识符,它们是保留的(google for details) 3. 请不要采用这种丑陋的旧 Microsoft winapi 风格,使用所有大写的类型名称。 4. 用 real cmets 代替视觉分隔符来解释函数的用途会更有帮助。
  • 我在做一些测试,你可以用 strcmp() 替换它
  • 除了区分大小写之外,但我想没关系(问题中没有指定和一个理智的假设),我就是这样做的。将我的观点 34 视为对一般风格的一些看法(无论你从中得到什么),但关于前导下划线的评论 可能不朽的:)
  • 我说的是你使用了一个名为_tmp的变量。
  • @FelixPalmen 感谢您的建议,我真的很感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多