This is the hardest problem ever since in this book. After some thinking, I realized that this problem can be solved with backtracking. After some more thinking, I realized that we should backtrack the cipher, i.e., the decrypt table along with the encrypt table. Unfortunately, one day has passed after these thinking.
The search procedure can be accelerated by three means:
1) index the dictionary by word length,
2) associate each word with a signature that specify the word's character pattern, and
3) sort the words by length in reverse order.
Code:
- /*************************************************************************
- * Copyright (C) 2008 by liukaipeng *
- * liukaipeng at gmail dot com *
- *************************************************************************/
- /* @JUDGE_ID 00000 843 C "Crypt Kicker" */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <strings.h>
- #define WORDSIZE 17
- #define LINESIZE 81
- #define NLINEWORDS (LINESIZE/2)
- #define NWORDS 1000
- #define NCHARS 128
- /*************************************************************************
- * Stack (FILO) *
- * Note: No error check is performed. *
- *************************************************************************/
- #define STACKSIZE NWORDS
- /* a cipher; used for backtracking */
- typedef struct cipher
- {
- int pos; /* the position of the current text in the line*/
- char decrypt[NCHARS]; /* the decrypt table to current*/
- char encrypt[NCHARS]; /* the encrypt table to current*/
- } cipher;
- typedef cipher valuetype;
- typedef struct stack
- {
- size_t top;
- valuetype values[STACKSIZE];
- } stack;
- void initstack(stack *s)
- {
- s->top = 0;
- }
- int emptystack(stack *s)
- {
- return s->top == 0;
- }
- void pushstack(stack *s, valuetype v)
- {
- s->values[s->top++] = v;
- }
- valuetype popstack(stack *s)
- {
- return s->values[--s->top];
- }
- /*************************************************************************/
- typedef int sig_t[WORDSIZE];
- /* a word in the dictionary */
- typedef struct dictword
- {
- char *word; /* the word */
- sig_t sig; /* the signature of the word */
- } dictword;
- /* a series of words in the dictionary */
- typedef struct dictwords
- {
- dictword words[NWORDS];
- int size;
- } dictwords;
- /* a dictionary indexed by the length of the words */
- typedef dictwords dict_t[WORDSIZE];
- void read_words(char words[][WORDSIZE], int nwords)
- {
- while (nwords-- > 0) {
- gets(words[nwords]);
- }
- }
- void make_sig(char *s, sig_t sig)
- {
- int trans[NCHARS] = {0};
- int i, t;
- for (i = 0, t = 1; s[i] != '/0'; ++i) {
- if (trans[s[i]]) {
- sig[i] = trans[s[i]];
- } else {
- sig[i] = trans[s[i]] = t++;
- }
- }
- }
- void make_dict(char words[][WORDSIZE], int nwords, dict_t dict)
- {
- int i;
- for (i = 0; i < WORDSIZE; ++i) {
- dict[i].size = 0;
- }
- for (i = 0; i < nwords; ++i) {
- dictword dw;
- dw.word = words[i];
- make_sig(dw.word, dw.sig);
- int len = strlen(words[i]);
- dict[len].words[dict[len].size++] = dw;
- }
- }
- int split(char line[], int size, char *texts[])
- {
- int i;
- int ntexts;
- for (i = 0, ntexts = 0; i < size; ++ntexts) {
- texts[ntexts] = line + i;
- for (; line[i] != ' ' && line[i] != '/0'; ++i);
- line[i++] = '/0';
- }
- return ntexts;
- }
- int strlencmp(void const *x, void const *y)
- {
- return strlen(*(char **)y) - strlen(*(char **)x);
- }
- char *decrypt(dict_t dict, char line[])
- {
- int i;
- int size = strlen(line);
- char *texts[LINESIZE];
- /* split the line into words */
- int ntexts = split(line, size, texts);
- /* sort by the word length: longest first */
- qsort(texts, ntexts, sizeof(char *), strlencmp);
- /* generate the signatures of the words */
- sig_t sigs[NLINEWORDS];
- for (i = 0; i < ntexts; ++i) {
- make_sig(texts[i], sigs[i]);
- }
- stack s;
- initstack(&s);
- cipher c = {0, {0}, {0}};
- pushstack(&s, c);
- /* backtracking with the ciphers */
- while (!emptystack(&s) && (c = popstack(&s), c.pos < ntexts)) {
- char *text = texts[c.pos];
- int len = strlen(text);
- dictwords *dws = &dict[len];
- for (i = 0; i < dws->size; ++i) {
- /* check the signature first to avoid unneccesary cipher checking */
- if (memcmp(sigs[c.pos], dws->words[i].sig, len*sizeof(int)) == 0) {
- cipher cc = c;
- char *word = dws->words[i].word;
- int j;
- for (j = 0; j < len; ++j) {
- if (!cc.decrypt[text[j]] && !cc.encrypt[word[j]]) {
- cc.decrypt[text[j]] = word[j];
- cc.encrypt[word[j]] = text[j];
- } else if (cc.decrypt[text[j]] != word[j]) {
- break;
- }
- }
- if (j == len) { /* cipher verified, track it */
- ++cc.pos;
- pushstack(&s, cc);
- }
- }
- }
- }
- if (c.pos != ntexts) { /* cannot be decrypted */
- memset(c.decrypt, '*', NCHARS);
- }
- c.decrypt['/0'] = ' ';
- for (i = 0; i < size; ++i) {
- line[i] = c.decrypt[line[i]];
- }
- return line;
- }
- int main(int argc, char *argv[])
- {
- #ifndef ONLINE_JUDGE
- char in[256];
- char out[256];
- strcpy(in, argv[0]);
- strcat(in, ".in");
- strcpy(out, argv[0]);
- strcat(out, ".out");
- if (freopen(in, "r", stdin) == NULL)
- fprintf(stderr, "%s: cannot open input file: %s./n", argv[0], in);
- if (freopen(out, "w", stdout) == NULL)
- fprintf(stderr, "%s: cannot open output file: %s./n", argv[0], out);
- #endif
- int nwords;
- scanf("%d/n", &nwords);
- char words[NWORDS][WORDSIZE];
- read_words(words, nwords);
- dict_t dict;
- make_dict(words, nwords, dict);
- char line[LINESIZE];
- while (gets(line) != NULL)
- puts(decrypt(dict, line));
- return 0;
- }