usaco6.3.2 Cryptcowgraphy

本文介绍了一种用于解密奶牛间传递的加密信息的方法,该方法涉及一系列复杂的字符串转换步骤,通过寻找特定字符组合来逐步还原原始消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 原题

Cryptcowgraphy
Brian Dean

The cows of Farmer Brown and Farmer John are planning a coordinated escape from their respective farms and have devised a method of encryption to protect their written communications.

Specifically, if one cow has a message, say, "International Olympiad in Informatics", it is altered by inserting the letters C, O, and W, in random location in the message, such that C appears before O, which appears before W. Then the cows take the part of the message between C and O, and the part between O and W, and swap them. Here are two examples:

            International Olympiad in Informatics
                              -> 
            CnOIWternational Olympiad in Informatics
            
            International Olympiad in Informatics
                              -> 
            International Cin InformaticsOOlympiad W

To make matters more difficult, the cows can apply their encryption scheme several times, by again encrypting the string that results from the previous encryption. One night, Farmer John's cows receive such a multiply-encrypted message. Write a program to compute whether or not the non-encrypted original message could have been the string:

            Begin the Escape execution at the Break of Dawn

PROGRAM NAME: cryptcow

INPUT FORMAT

A single line (with both upper and lower case) with no more than 75 characters that represents the encrypted message.

SAMPLE INPUT (file cryptcow.in)

Begin the EscCution at the BreOape execWak of Dawn

OUTPUT FORMAT

Two integers on a single line. The first integer is 1 if the message decodes as an escape message; 0 otherwise. The second integer specifies the number of encryptions that were applied (or 0 if the first integer was 0).

SAMPLE OUTPUT (file cryptcow.out)

1 1



二 分析

这是一道让我无比郁闷的搜索题,总体思路而言就是每次找出一组(C,O,W)对,将字符串进行一次还原,看最后能否得到初始字符串。最多有9次还原操作,搜索空间大小是(9!)^3级别。我使用了三个剪枝:
1. 如果当前字符串长度减去初始字符串长度的结果不能被3整除,则无解;
2. 搜索中会重复访问字符串,因此需要hash,我们在得到解的时候立刻返回,因此如果遇到重复过的字符串就代表无解;
3. 当前字符串中连续的非C,O,W的子串,一定保持着与初始字符串相同的顺序,否则无解;

2,3剪枝都很重要,1因为好想与好写就顺带写上了,并没有测验必要性。AC代码1是一个较粗糙的实现。

粗糙的地方1在于hash的写法,首先如果hash不处理冲突的话,代码理论上是不对的,一个可行的字符串可能因为与先前访问过的不可行的字符串hash到了同一个值而导致漏解(即明明有答案,但会输出0 0);其次hash的范围取多大会严重影响代码的运行速度。
(AC代码1如果把1,3剪枝去掉,即注释掉对impossible函数的调用,是无法通过“BeCOgC CC execuOf DOBCiCCrWaOOt theCOCeak oWWin Oon aWtheOOW EscapeWtWWWwn”这组测试样例的,注释后的代码会输出'0 0',而实际上这个输入是有解的,需要9次变换。这是一个很有趣的现象,去掉了剪枝,扩大了搜索范围,反而找不到正确的解了,这是因为正确搜索路径上的字符串可能被错误的字符串提前抢占了hash值,导致了不该发生的hash剪枝。)

粗糙的地方2在于transform函数的写法,在这种写法中一次transform的时间是O(n)的,n为字符串的长度;而如果我们用链表存储字符串的话,在找到一组C,O,W的位置后,可以在O(1)的时间内完成transform。


三 代码

运行结果1:
USER: Qi Shen [maxkibb3]
TASK: cryptcow
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 6136 KB]
   Test 2: TEST OK [0.000 secs, 6136 KB]
   Test 3: TEST OK [0.000 secs, 6136 KB]
   Test 4: TEST OK [0.000 secs, 6136 KB]
   Test 5: TEST OK [0.000 secs, 6136 KB]
   Test 6: TEST OK [0.042 secs, 6136 KB]
   Test 7: TEST OK [0.000 secs, 6136 KB]
   Test 8: TEST OK [0.056 secs, 6136 KB]
   Test 9: TEST OK [0.980 secs, 6136 KB]
   Test 10: TEST OK [1.232 secs, 6136 KB]

All tests OK.

Your program ('cryptcow') produced all correct answers! This is your submission #18 for this problem. Congratulations!


AC代码1:
/*
ID:maxkibb3
LANG:C++
PROB:cryptcow
*/

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

const int HASH_SIZE = 1e6 + 7;

bool table[HASH_SIZE], table2[HASH_SIZE];
string s, t = "Begin the Escape execution at the Break of Dawn";

int getHash(const string &str) {
    unsigned long h = 0, g;
    for(int i = 0; i < str.length(); i++) {
        h = (h << 4) + str[i];
        g = h & 0xf0000000l;
        if(g) h ^= g >> 24;
        h &= ~g;
    }
    return h % HASH_SIZE;
}

string transform(const string &str, int c, int o, int w) {
    int idx = 0, len = str.length();
    static char buffer[80];
    for(int i = 0; i < c; i++) buffer[idx++] = str[i];
    for(int i = o + 1; i < w; i++) buffer[idx++] = str[i];
    for(int i = c + 1; i < o; i++) buffer[idx++] = str[i];
    for(int i = w + 1; i < len; i++) buffer[idx++] = str[i];
    buffer[idx++] = 0;
    return string(buffer);
}

bool impossible(const string &str) {
    int len = str.length();
    if((len - t.length()) % 3 != 0) return true;
    int i = 0, j;
    while(i < len) {
        j = i + 1;
        if(str[i] != 'C' && str[i] != 'O' && str[i] != 'W') {
            while(j < len) {
                if(str[j] == 'C' || str[j] == 'O' || str[j] == 'W')
                    break;
                j++;
            }
            if(t.find(str.substr(i, j - i)) == string::npos)
                return true;
        }
        i = j;
    }
    return false;
}

bool dfs(string str) {
    int hash = getHash(str);
    if(table[hash]) return false;
    table[hash] = true;

    if(impossible(str)) return false;

    if(str == t) return true;

    int len = str.length();
    for(int o = 1; o < len - 1; o++) {
        if(str[o] != 'O') continue;
        for(int c = 0; c < o; c++) {
            if(str[c] != 'C') continue;
            for(int w = len - 1; w > o; w--) {
                if(str[w] != 'W') continue;
                if(dfs(transform(str, c, o, w)))
                    return true;
            }
        }
    }

    return false;
}

int main() {
    freopen("cryptcow.in", "r", stdin);
    freopen("cryptcow.out", "w", stdout);
    getline(cin, s);

    if(dfs(s)) {
        cout << "1 " << count(s.begin(), s.end(), 'C') << endl;
    }
    else {
        cout << "0 0" << endl;
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值