Вопрос о строках и характере персонажей для гуру - PullRequest
6 голосов
/ 10 февраля 2010

Вот проблема, которая поставила меня в тупик (мудрое решение):

С учетом str S, применить сопоставления символов Cm = {a=(m,o,p),d=(q,u),...} и распечатать все возможные комбинации, используя C или C ++.

Строка может быть любой длины, количество сопоставлений символов варьируется, и не будет отображений, сопоставляющих другую карту (таким образом, избегая циклических зависимостей).

В качестве примера: строка abba с отображениями a=(e,o), d=(g,h), b=(i) выдаст:

abba,ebba,obba,abbe,abbo,ebbe,ebbo,obbe,obbo,aiba,aiia,abia,eiba,eiia,...... 

Ответы [ 6 ]

2 голосов
/ 10 февраля 2010

РЕДАКТИРОВАТЬ: Это должен быть самый быстрый и простой алгоритм. Некоторые могут спорить со стилем или переносимостью; Я думаю, что это идеально подходит для вещей встроенного типа, и я уже потратил достаточно времени на это. Я оставляю оригинал ниже.

Это использует массив для отображения. Знаковый бит используется для указания конца цикла отображения, поэтому тип массива должен быть больше отображаемого типа, если вы хотите использовать полный диапазон unsigned.

Генерирует 231M строк / с или ~ 9,5 циклов / строк на 2,2 ГГц Core2. Условия тестирования и использования, как показано ниже.

#include <iostream>
using namespace std;

int const alphabet_size = CHAR_MAX+1;
typedef int map_t; // may be char or short, small performance penalty
int const sign_bit = 1<< CHAR_BIT*sizeof(map_t)-1;
typedef map_t cmap[ alphabet_size ];

void CreateMap( char *str, cmap &m ) {
    fill( m, m+sizeof(m)/sizeof(*m), 0 );
    char *str_end = strchr( str, 0 ) + 1;
    str_end[-1] = ' '; // space-terminated strings
    char prev = ' ';
    for ( char *pen = str; pen != str_end; ++ pen ) {
        if ( * pen == ' ' ) {
            m[ prev ] |= sign_bit;
            prev = 0;
        }
        m[ * pen ] = * pen;
        if ( prev != ' ' ) swap( m[prev], m[ *pen ] );
        prev = *pen;
    }
    for ( int mx = 0; mx != sizeof(m)/sizeof(*m); ++ mx ) {
        if ( m[mx] == 0 ) m[mx] = mx | sign_bit;
    }
}

bool NextMapping( char *s, char *s_end, cmap &m ) {
    for ( char *pen = s; pen != s_end; ++ pen ) {
        map_t oldc = *pen, newc = m[ oldc ];
        * pen = newc & sign_bit-1;
        if ( newc >= 0 ) return true;
    }
    return false;
}

int main( int argc, char **argv ) {
    uint64_t cnt = 0;
    cmap m;
    CreateMap( argv[1], m );
    char *s = argv[2], *s_end = strchr( s, 0 );
    do {
        ++ cnt;
    } while ( NextMapping( s, s_end, m ) );
    cerr << cnt;
    return 0;
}

ОРИГИНАЛ:

Не такой короткий или крепкий, как хотелось бы, но вот что-то.

  • Требуется, чтобы входная строка всегда содержала первую букву в алфавитном порядке в каждом наборе замены
  • Выполнить а-ля maptool 'aeo dgh bi' abbd
  • Вывод в обратном лексикографическом порядке
  • Производительность около 22 циклов / строка (100M строк / сек при 2,2 ГГц Core2)
    • НО моя платформа пытается быть умной с string s, замедляя ее
    • Если я изменю его на использование char* строк вместо этого, он будет работать с 142M строк / сек (~ 15,5 циклов / строка)
  • Должна быть возможность двигаться быстрее, используя таблицу сопоставления char[256] и другую char[256], указывающую, какие символы заканчивают цикл.

Структура данных карты представляет собой массив узлов, связанных в круговые списки.

#include <iostream>
#include <algorithm>
using namespace std;

enum { alphabet_size = UCHAR_MAX+1 };

struct MapNode {
     MapNode *next;
     char c;
     bool last;
     MapNode() : next( this ), c(0), last(false) {}
};

void CreateMap( string s, MapNode (&m)[ alphabet_size ] ) {
    MapNode *mprev = 0;
    replace( s.begin(), s.end(), ' ', '\0' );
    char *str = const_cast<char*>(s.c_str()), *str_end = str + s.size() + 1;
    for ( char *pen = str; pen != str_end; ++ pen ) {
        if ( mprev == 0 ) sort( pen, pen + strlen( pen ) );
        if ( * pen == 0 ) {
            if ( mprev ) mprev->last = true;
            mprev = 0;
            continue;
        }
        MapNode &mnode = m[ * pen ];
        if ( mprev ) swap( mprev->next, mnode.next ); // link node in
        mnode.c = * pen; // tell it what char it is
        mprev = &mnode;
    }
       // make it easier to tell that a node isn't in any map
    for ( MapNode *mptr = m; mptr != m + alphabet_size; ++ mptr ) {
        if ( mptr->next == mptr ) mptr->next = 0;
    }
}

bool NextMapping( string &s, MapNode (&m)[ alphabet_size ] ) {
    for ( string::iterator it = s.begin(); it != s.end(); ++ it ) {
        MapNode &mnode = m[ * it ];
        if ( mnode.next ) {
            * it = mnode.next->c;
            if ( ! mnode.last ) return true;
        }
    }
    return false;
}

int main( int argc, char **argv ) {
    MapNode m[ alphabet_size ];
    CreateMap( argv[1], m );
    string s = argv[2];
    do {
        cerr << s << endl;
    } while ( NextMapping( s, m ) );
 return 0;
}
2 голосов
/ 10 февраля 2010

Определенно возможно, не очень сложно ... но это сгенерирует много строк, это точно.

Первое, на что стоит обратить внимание, это то, что вы знаете, сколько строк он собирается сгенерировать заранее, поэтому легко выполнить некоторую проверку работоспособности:)

Второе: похоже, что рекурсивное решение будет простым (как и многие проблемы прохождения).

class CharacterMapper
{
public:
  CharacterMapper(): mGenerated(), mMapped()
  {
    for (int i = -128, max = 128; i != max; ++i)
      mMapped[i].push_back(i); // 'a' is mapped to 'a' by default
  }

  void addMapped(char origin, char target)
  {
    std::string& m = mMapped[origin];
    if (m.find(target) == std::string::npos) m.push_back(target);
  } // addMapped

  void addMapped(char origin, const std::string& target)
  {
    for (size_t i = 0, max = target.size(); i != max; ++i) this->addMapped(origin, target[i]);
  } // addMapped

  void execute(const std::string& original)
  {
    mGenerated.clear();
    this->next(original, 0);
    this->sanityCheck(original);
    this->print(original);
  }

private:
  void next(std::string original, size_t index)
  {
    if (index == original.size())
    {
      mGenerated.push_back(original);
    }
    else
    {
      const std::string& m = mMapped[original[index]];
      for (size_t i = 0, max = m.size(); i != max; ++i)
        this->next( original.substr(0, index) + m[i] + original.substr(index+1), index+1 );
    }
  } // next

  void sanityCheck(const std::string& original)
  {
    size_t total = 1;
    for (size_t i = 0, max = original.size(); i != max; ++i)
      total *= mMapped[original[i]].size();

    if (total != mGenerated.size())
      std::cout << "Failure: should have found " << total << " words, found " << mGenerated.size() << std::endl;
  }

  void print(const std::string& original) const
  {
    typedef std::map<char, std::string>::const_iterator map_iterator;
    typedef std::vector<std::string>::const_iterator vector_iterator;

    std::cout << "Original: " << original << "\n";

    std::cout << "Mapped: {";
    for (map_iterator it = mMapped.begin(), end = mMapped.end(); it != end; ++it)
      if (it->second.size() > 1) std::cout << "'" << it->first << "': '" << it->second.substr(1) << "'";
    std::cout << "}\n";

    std::cout << "Generated:\n";
    for (vector_iterator it = mGenerated.begin(), end = mGenerated.end(); it != end; ++it)
      std::cout << "  " << *it << "\n";
  }

  std::vector<std::string> mGenerated;
  std::map<char, std::string> mMapped;
}; // class CharacterMapper


int main(int argc, char* argv[])
{
  CharacterMapper mapper;
  mapper.addMapped('a', "eo");
  mapper.addMapped('d', "gh");
  mapper.addMapped('b', "i");
  mapper.execute("abba");
}

А вот и вывод:

Original: abba
Mapped: {'a': 'eo''b': 'i''d': 'gh'}
Generated:
  abba
  abbe
  abbo
  abia
  abie
  abio
  aiba
  aibe
  aibo
  aiia
  aiie
  aiio
  ebba
  ebbe
  ebbo
  ebia
  ebie
  ebio
  eiba
  eibe
  eibo
  eiia
  eiie
  eiio
  obba
  obbe
  obbo
  obia
  obie
  obio
  oiba
  oibe
  oibo
  oiia
  oiie
  oiio

Да, довольно долго, но есть много вещей, которые напрямую не участвуют в вычислениях (инициализация, проверки, печать). Основными методами является next, который реализует рекурсию.

1 голос
/ 10 февраля 2010

Способ, которым я хотел бы сделать это, состоит в том, чтобы создать массив индексов той же длины, что и строка, все инициализированные в нуле.Затем мы рассматриваем этот массив индексов как счетчик для перечисления всех возможных отображений нашей исходной строки.Индекс 0 отображает эту позицию в строке на первое сопоставление для этого символа, 1 на второй и т. Д. Мы можем пройти их по порядку, просто увеличив последний индекс в массиве, перенося его на следующую позицию, когда мыдостичь максимального числа отображений для этой позиции.

Чтобы использовать ваш пример, у нас есть отображения

'a' => 'e', 'o'
'b' => 'i'

Со входной строкой "abba" нам нужен массив из четырех элементов для нашегоindexes:

[0,0,0,0] => "abba"
[0,0,0,1] => "abbe"
[0,0,0,2] => "abbo"
[0,0,1,0] => "abia"
[0,0,1,1] => "abie"
[0,0,1,2] => "abio"
[0,1,0,0] => "aiba"
[0,1,0,1] => "aibe"
[0,1,0,2] => "aibo"
[0,1,1,0] => "aiia"
[0,1,1,1] => "aiie"
[0,1,1,2] => "aiio"
[1,0,0,0] => "ebba"
[1,0,0,1] => "ebbe"
[1,0,0,2] => "ebbo"
[1,0,1,0] => "ebia"
[1,0,1,1] => "ebie"
[1,0,1,2] => "ebio"
[1,1,0,0] => "eiba"
[1,1,0,1] => "eibe"
[1,1,0,2] => "eibo"
[1,1,1,0] => "eiia"
[1,1,1,1] => "eiie"
[1,1,1,2] => "eiio"
[2,0,0,0] => "obba"
[2,0,0,1] => "obbe"
[2,0,0,2] => "obbo"
[2,0,1,0] => "obia"
[2,0,1,1] => "obie"
[2,0,1,2] => "obio"
[2,1,0,0] => "oiba"
[2,1,0,1] => "oibe"
[2,1,0,2] => "oibo"
[2,1,1,0] => "oiia"
[2,1,1,1] => "oiie"
[2,1,1,2] => "oiio"

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

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

#include <string.h>
#include <stdlib.h>
int each_combination( 
    char const * source, 
    char const * mappings[256],
    int (*callback)(char const *, void *), 
    void * thunk
) {
  if (mappings == NULL || source == NULL || callback == NULL )
  {
    return -1;
  }
  else
  {
    size_t i;
    int rv;
    size_t num_mappings[256] = {0};
    size_t const source_len = strlen(source);
    size_t * const counter = calloc( source_len, sizeof(size_t) );
    char * const scratch = strdup( source );

    if ( scratch == NULL || counter == NULL )
    {
      rv = -1;
      goto done;
    }

    /* cache the number of mappings for each char */
    for (i = 0; i < 256; i++)
      num_mappings[i] = 1 + (mappings[i] ? strlen(mappings[i]) : 0);

    /* pass each combination to the callback */
    do {
      rv = callback(scratch, thunk);
      if (rv != 0) goto done;

      /* increment the counter */
      for (i = 0; i < source_len; i++)
      {
        counter[i]++;
        if (counter[i] == num_mappings[(unsigned char) source[i]])
        {
          /* carry to the next position */
          counter[i] = 0;
          scratch[i] = source[i];
          continue;
        }
        /* use the next mapping for this character */
        scratch[i] = mappings[(unsigned char) source[i]][counter[i]-1];
        break;
      }
    } while(i < source_len);

done:
    if (scratch) free(scratch);
    if (counter) free(counter);
    return rv;
  }
}
#include <stdio.h>
int print_each( char const * s, void * name)
{
    printf("%s:%s\n", (char const *) name, s);
    return 0;
}
int main(int argc, char ** argv)
{
  char const * mappings[256] = { NULL };
  mappings[(unsigned char) 'a'] = "eo";
  mappings[(unsigned char) 'b'] = "i";

  each_combination( "abba", mappings, print_each, (void *) "abba");
  each_combination( "baobab", mappings, print_each, (void *) "baobab");

  return 0;
}
0 голосов
/ 12 августа 2011

простая рекурсивная перестановка с использованием char char [256]

char *map [256];

/* permute the ith char in s */
perm (char *s, int i)
{
   if (!s) return;

   /* terminating condition */
   if (s[i] == '\0') {
      /* add "s" to a string array if we want to store the permutations */
      printf("%s\n", s);
      return;
   }

   char c = s[i];
   char *m = map [c];
   // printf ("permuting at [%c]: %s\n", c, m);
   int j=0;
   /* do for the first char, then use map chars */
   do {
      perm (s, i+1);
      s[i] = m[j];
   } while (m[j++] != '\0');
   /* restore original char here, used for mapping */
   s[i] = c;

   return;
}

int main ()
{
   /* map table initialization */
   map['a'] = "eo\0";
   map['b'] = "i\0";
   map['d'] = "gh\0";

   /* need modifyable sp, as we change chars in position, sp="abba" will not work! */
   char *sp = malloc (10);
   strncpy (sp, "abba\0", 5);

   perm (sp, 0);
   return 0;
}
0 голосов
/ 10 февраля 2010

Есть ссылка на архив фрагментов, который делает это здесь Permute2.c . Существует еще один вариант перестановки строк (я думаю, вы могли бы отфильтровать те, которых нет на карте!) Смотрите здесь в архиве snippets '...

Надеюсь, это поможет, С наилучшими пожеланиями, Том.

0 голосов
/ 10 февраля 2010

Вы, по сути, хотите выполнить поиск в глубину (DFS) или любой другой обход вниз направленного ациклического графа слов (DAWG). Я выложу код в ближайшее время.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...