Как разделить строку на массив уникальных символов / строк

В основном я хочу знать, можно ли (если да, то как) читать строку слева направо и жадно завершать и добавлять, как только будет найдена новая строка. Например.

"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;
                }

            }
        }

}

person E. Munch    schedule 18.10.2015    source источник
comment
Это можно сделать? Конечно, многие strstr() в конечном итоге должны это сделать. Можно ли это сделать эффективно? Без понятия.   -  person EOF    schedule 18.10.2015
comment
Я не совсем понимаю, почему вам нужны BC и ABC, а не BCA?   -  person Maxime Chéramy    schedule 18.10.2015
comment
Добро пожаловать в Stack Overflow. Вскоре прочтите страницу О программе. Вы должны показать нам код, который, по вашему мнению, лучше всего справляется с этой задачей, и показать, что он производит до того, как он выйдет из строя (у вас есть хоть какой-то диагностический результат, не так ли?), Чтобы мы могли видеть то, что вы видите. Мы с радостью поможем вам решить проблемы в вашем коде. Мы не будем просто писать вашу программу за вас.   -  person Jonathan Leffler    schedule 18.10.2015
comment
@maxime: когда код читается как A, он новый; B новый; C новый. Затем он читает новое A, но это не ново, поэтому он также читает B, поскольку AB является новым; затем читается CA; тогда BC новый, и, наконец, AB не новый, а ABC.   -  person Jonathan Leffler    schedule 18.10.2015
comment
Может быть, три? en.wikipedia.org/wiki/Trie   -  person    schedule 18.10.2015
comment
О, я вижу. OP нужно только использовать хеш-набор и затем выполнить итерацию.   -  person Maxime Chéramy    schedule 18.10.2015
comment
Если ваш целевой (выходной) массив определен как char *a[linelen];, тогда проблема просто в том, что вы обращаетесь к неинициализированной памяти - у вас есть массив указателей, но эти указатели никуда не указывают, и вы никогда не выделяете им память, чтобы они указывали на . Подумайте о написании собственного специального варианта strdup() с такой подписью, как char *dup_char_range(const char *start, int len); - это не требует ввода строки с завершающим нулем, что, вероятно, подходит для вашей проблемы.   -  person Jonathan Leffler    schedule 18.10.2015
comment
Не могли бы вы хотя бы опубликовать полную функцию, которую вы используете? Код без контекста бесполезен.   -  person EOF    schedule 18.10.2015
comment
@EOF Единственное, что я не публиковал, - это задания / определения. Вы видите все, что у меня есть, кроме int i; и Т. Д.   -  person E. Munch    schedule 18.10.2015
comment
@ Э.Мунк: И никакого прототипа функции, аргументов, типа возврата ...   -  person EOF    schedule 18.10.2015
comment
Просмотрите, как создать MCVE (Как создать минимальный, полный и проверяемый пример?). Это то, с чем нам нужно дать работать. Обратите внимание, что одна из причин, по которой мы хотим увидеть ваш код, заключается в том, что он дает нам возможность увидеть, на каком уровне вы кодируете, поэтому мы можем избежать предложения решений, которые выходят далеко за рамки вашего уровня понимания. Вам нужно будет использовать некоторую функцию сравнения строк; просто сравнение указателей не сработает. OTOH, strcmp() требует строк с завершающим нулем. Я думаю, что ваш код не завершает строки нулем.   -  person Jonathan Leffler    schedule 18.10.2015
comment
Учитывая строку длины L, вам понадобится массив также длины L в том случае, если все символы уникальны. Этот массив будет содержать две части информации: указатель на первый символ уникальной подстроки и длину подстроки. Сравнение будет выполняться с использованием memcmp или strncmp из-за того, что вы не можете завершать каждую подстроку нулевым символом (иначе вы разделите ababc на ab и bc, перезаписав a в abc). Если вы хотите использовать два отдельных массива, один для размеров, а другой для указателей, вы также можете это сделать. Это решение предполагает, что вы не знаете malloc.   -  person    schedule 18.10.2015
comment
@ E.Munch, пожалуйста, не удаляйте части, которые необходимы для понимания уже имеющихся комментариев / ответов, спасибо. Кстати, интересная проблема, работаю над решением просто для удовольствия, но я сомневаюсь, что это было бы полезно без подробных объяснений.   -  person    schedule 18.10.2015
comment
Я добавил тесты для всех программ в конце своего ответа, если вам интересно.   -  person Craig Estey    schedule 19.10.2015


Ответы (5)


Вот тот, который использует простой массив символов для хранения «видимых» строк:

#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;
}

Вот несколько тестов для любой программы (время в нс и printf nop'ed):

       first      minimum author
         527          137 craig1 -- original -- uses single seen char array
         146           39 craig2 -- modified -- uses 2D seen table
       45234        45234 felix1 -- original -- may only be executed once
       40460          656 felix2 -- uses fixed input
          24           18 machine1 -- original -- uses buffer[20][20] on stack
         908          417 machine2 -- modified -- uses global buffer[20][20]
       43089         1120 milevyo1 -- original
       42719          711 milevyo2 -- parseString tmp is stack buffer no malloc
        7957          429 milevyo3 -- NewNode uses fixed pool no malloc
        7457          380 milevyo4 -- removed linked list
person Craig Estey    schedule 19.10.2015
comment
Да, это интересно, хотя и не совсем сопоставимо, потому что не все решения обладают одинаковой гибкостью. Ваш подход к хранению всего этого непрерывно, вероятно, отлично подходит для локализации кеша. Обычно много времени тратится на выделение кучи. Интересно, может ли моя идея иметь ведра на длину строки помочь с масштабированием. - person ; 19.10.2015
comment
@FelixPalmen Добавлен новый способ, который в 4 раза быстрее. Ты прав. Горячий кеш важен. В таблице левый столбец соответствует первому разу (кэш холодный), а второй - лучшему из N запусков (кэш горячий). Ваше происхождение потеряно на стандартном вводе. Я подправил его и другие (например, doit (ABCABCABCABC)) - значительное ускорение. Обратите внимание, что для меня холодный кеш и -DBIG медленный, но горячий кеш - то же самое. Ваши ведра похожи на хэш. Если да, будет ли strlen + first char лучшим ключом? (например, таблица поиска 2D) Или мне это не хватает? - person Craig Estey; 20.10.2015
comment
Использование цикла для управления тестами (например, for/continue, for( ... i+=step), или даже goto), безусловно, является наиболее эффективным способом решения ряда итеративных ситуаций. Хорошая мысль. - person David C. Rankin; 20.10.2015
comment
@ DavidC.Rankin Спасибо. Я потерпел неудачу в вызове Google foobar, игра со скучающими миньонами. Правильный способ был формой динамического программирования, поэтому провел некоторые исследования и начал искать варианты использования: см. Мой ответ в проблеме вампира stackoverflow.com/questions/32834843/ < / а>. Для текущей проблемы я просто органично придумал второй алгоритм, но я думаю, что это динамическое программирование с табуляцией [методика, позволяющая избежать дорогостоящего поиска в списке / дереве] и получить O (n) против O (n ^ 2) или хуже. - person Craig Estey; 21.10.2015

Да, это возможно.

Вам просто нужно отслеживать различные вхождения строк. Самый простой способ - использовать Set.

Изменить: см. Здесь, как реализовать набор в C: Как реализовать набор структура данных

Edit2: вы можете найти здесь реализацию (я ее не тестировал): HashSet.c

person ESala    schedule 18.10.2015
comment
Я полагаю, что это прямой и правильный ответ на вопрос, но он не очень полезен. Какой стандартный заголовок C предоставляет тип Set? - person Jonathan Leffler; 18.10.2015
comment
@JonathanLeffler, довольно легко переопределить простой хеш-набор. Вам нужен только массив связанных списков и хеш-функция. - person Maxime Chéramy; 18.10.2015
comment
@Maxime: нам нужно увидеть код OP, прежде чем мы сможем оценить, находится ли массив связанных списков с динамическим распределением памяти (и даже структур) в пределах его знаний. Мне кажется, что такие предложения скорее сбивают его с толку, чем помогают - его знания еще не на этом уровне. Надеюсь, я ошибаюсь, но, не видя их кода, никто не может быть уверен. - person Jonathan Leffler; 18.10.2015
comment
Спасибо за ответ, но я понятия не имею, что это за набор и как его реализовать. Я быстро погуглил, и я не могу понять, как это можно легко реализовать - person E. Munch; 18.10.2015
comment
@ E.Munch, я отредактировал ответ, чтобы добавить дополнительную информацию. - person ESala; 18.10.2015
comment
@JonathanLeffler Я редактировал имеющимся у меня кодом. - person E. Munch; 18.10.2015
comment
С опубликованным кодом первые два предложения этого ответа остаются точными. Ссылка x-ref на вопрос SO интересна (спасибо за это), но использование наборов является излишним для контекста. - person Jonathan Leffler; 18.10.2015

Вот так должно получиться:

#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]);
    }
}
person machine_1    schedule 18.10.2015
comment
Я добавил тесты для всех программ в конце своего ответа, если вам интересно. - person Craig Estey; 19.10.2015

Вот полностью динамическое решение на 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'
person Community    schedule 18.10.2015
comment
Я добавил тесты для всех программ в конце своего ответа, если вам интересно. - person Craig Estey; 19.10.2015

Я использую связанный список, чтобы избежать дублирования элементов. проверьте вывод в конце.

#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"}
person milevyo    schedule 19.10.2015
comment
Связанный список, вероятно, является одним из наименее эффективных подходов, тем не менее, пока он дает правильный результат, это ответ :). Тем не менее некоторые комментарии к коду: 1. Что такое _stricmp()? Нестандартный и, поскольку остальная часть кода соответствует стандарту, зачем вводить это? 2. Избегайте использования идентификаторов, начинающихся с символа подчеркивания в C, они зарезервированы (подробности можно найти в Google) 3. Пожалуйста, не принимайте этот уродливый старый стиль Microsoft winapi, в котором используются все имена типов в верхнем регистре. 4. Вместо визуальных разделителей более полезными были бы настоящие комментарии, объясняющие назначение функции. - person ; 19.10.2015
comment
я делал несколько тестов, вы просто можете заменить его на strcmp () - person milevyo; 19.10.2015
comment
за исключением того, что это будет чувствительно к регистру, но я думаю, что это нормально (не указано в вопросе и разумное предположение), я просто сделал это таким образом. Примите мои точки 3 и 4 как некоторое мнение о стиле в целом (что бы вы из него ни сделали), но замечание о ведущих подчеркиваниях может быть важно :) - person ; 19.10.2015
comment
Я говорю об использовании вами переменной с именем _tmp. - person ; 19.10.2015
comment
@FelixPalmen благодарим вас за советы, я очень ценю это. - person milevyo; 19.10.2015
comment
Я добавил тесты для всех программ в конце своего ответа, если вам интересно. - person Craig Estey; 19.10.2015