[usaco] 4.1.4 PROB Cryptcowgraphy

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


----------------------------------------------------
解法dfs+elfhash。
基本原理就是暴搜,但是要加上減枝。
以下減枝:
1. 由於添加的COW是一起的,因此給出的字符串的字符個數應該等於47(目標字符串的長度)+3*k。如果不滿足就可直接判斷無解。
2. 除了COW三個字符外,其他的字符的個數應該和目標串相一致。如果不一致也可直接判斷無解。
   搜索中間肯定會出現很多相同的情況,因此需要開一個hash來記錄搜索到過哪些字符串,每搜索到一個字符串,就判重。
   如果重複直接剪枝。這裏的字符串的hash函數可以採用ELFhash,但由於ELFhash的數值太大,
   所以用函數值對一個大質數(我用的是35023)取餘,這樣可以避免hash開得太大,同時又可以減少衝突。
   實驗證明,加上elfhash之後,最複雜的情況用時也只有0.243.
3. 對搜索到的字符串,設不包含COW的最長前綴爲n前綴(同樣也可以定義n後綴),那麼如果n前綴不等於目標串的長度相同的前綴,
   那麼當前字符串一定無解,剪枝。N後綴也可採取相同的判斷方法。
4. 一個有解的字符串中,COW三個字母最早出現的應該是C,最後出現的應該是W,如果不滿足則剪枝。
5 . 當前字符串中任意兩個相鄰的COW字母中間所夾的字符串一定在目標串中出現過。如果不符合可立即剪枝。
6. 首先枚舉o的位置,那麼c的位置已經在0和O之間了,w的位置已經在o之後。
7. 在判斷當前串的子串是否包含在目標串中的時候,可以先做一個預處理:記錄每一個字母曾經出現過的位置,然後可以直接枚舉子串的第一個字母的位置。這樣比用pos要快2倍左右。
8. 判斷某個字符是否在原始串中。給每一個字符計算一個hash值,爲其上一個字符左移n位加上當前字符的值。這樣就很容易個判斷是否含有這個字符串了

------------------------------------------

/*
ID:yunleis2
PROG:cryptcow
LANG:C++
*/

#include<iostream>
#include<fstream>
#include<string.h>
#include<ctime>
using namespace std;
char origin[]="Begin the Escape execution at the Break of Dawn";
int hashvalue[60];
#define  HASHSIZE 871
int hashpos[3][HASHSIZE];
char encode[80];
int cnum,onum,wnum;
long begintime;
#define isencodechar(ch)   (ch=='C'||ch=='O'||ch=='W')
int length;
int orilen;
#define MAXHASH 35023
bool hashsearch[MAXHASH];
inline bool exists(char  enc[],int begin,int end){
	for(int i=begin+1;i<=end;i++){
		
		int hashvalue=enc[i-1]<<5;
		hashvalue+=enc[i];
		hashvalue%=HASHSIZE;
		bool flag=false;
		for(int j=0;j<3;j++){
			int pos=hashpos[j][hashvalue];
			if(pos==-1)
				break;
			if(pos!=-1&&origin[pos]==enc[i])
				flag=true;
		}
		if(!flag)
			return false;
	}
	return true;
}
fstream fin("cryptcow.in",ios::in );
fstream fout("cryptcow.out",ios::out);
void search(char * encode,int depth);
int main()
{
	
	fin.getline(encode,80);
	cout<<encode<<endl;
	//check header;
	cnum=onum=wnum=0;
	for(int i=0;i<3*HASHSIZE;i++){
		hashpos[i/HASHSIZE][i%HASHSIZE]=-1;
	}
	char value[70];
	length=strlen(encode);
	orilen=strlen(origin);
	for(int i=0;i<length;i++){
		if(encode[i]=='C')
			++cnum;
		if(encode[i]=='O')
			++onum;
		if(encode[i]=='W')
			++wnum;
		if(!isencodechar(encode[i])){
			if(encode[i]==' ')
				encode[i]=91;
			encode[i]-=64;
		}
		if(i<orilen){
			if(origin[i]==' ')
				origin[i]=91;
			origin[i]-=64;
			int value=0;
			if(i!=0){
				value=origin[i-1]<<5;
			}
			value+=origin[i];
			hashvalue[i]=value;
			value%=HASHSIZE;
			if(hashpos[0][value]==-1)
				hashpos[0][value]=i;
			else if(hashpos[1][value]==-1)
				hashpos[1][value]=i;
			else  if(hashpos[2][value]==-1)
				hashpos[2][value]=i;
			else
				cout<<"error"<<value<<" "<<i;
		}
	}
	if((length-orilen)%3!=0){
		fout<<0<<" "<<0<<endl;
		return 0;
	}
	int last=-1;
	for(int i=0;i<length;i++){
		 
		if(encode[i]=='C')
		{ 
			last=i;
		}
		if(encode[i]=='O')
		{
			if(!exists(encode,last+1,i-1)){
				fout<<0<<" "<<0<<endl;
				return 0;
			}
			last=i;
		}
		if(encode[i]=='W')
		{
			 
			if(!exists(encode,last+1,i-1)){
				fout<<0<<" "<<0<<endl;
				return 0;
			}
			last=i;
		}
	}

	if(cnum!=onum||cnum!=wnum){
		fout<<0<<" "<<0<<endl;
		return 0;
	}

	bool flag=true;
	for(int i=0;i<length&&i<orilen;i++){
		if(encode[i]=='C')
			break;
		else if(encode[i]!=origin[i])
		{
			fout<<0<<" "<<0<<endl;
			return 0;
		}
	}
	for(int i=0;i<length&&i<orilen;i++){
		if(encode[length-i-1]=='W')
			break;
		else if(encode[length-i-1]!=origin[orilen-i-1]){
			fout<<0<<" "<<0<<endl;
			return 0;
		}
	}

#if 0
	for(int i=0;i<length;i++){
		cout<<(int)encode[i]<<" ";
	}
	cout<<endl;
	for(int i=0;i<orilen;i++){
		cout<<(int)origin[i]<<" ";
	}
	cout<<endl;
	for(int i=0;i<orilen;i++){
		cout<<hashvalue[i]<<" ";
	}
#endif
	 begintime=clock();
	search(encode,cnum);
	
	fout<<0<<" "<<0<<endl;
	//system("pause");
}
unsigned long
	elf_hash( char *name)
{
	unsigned long       h = 0, g;

	while (*name) {
		h = (h << 4) + *name++;
		if (g = h & 0xf0000000)
			h ^= g >> 24;
		h &= ~g;
	}
	return h;
}
void search(char * encode,int depth){
 
	//cout<<<<endl;
	long hash =elf_hash(encode)%MAXHASH;
	if(hashsearch[hash])
	{
		cout<<encode<<endl;
		return;
	}
	hashsearch[hash]=true;
	char tmp[3];
	char *decode=new char[strlen(encode)+1];
	if(depth==0){
#if 0
		for(int i=0;i<strlen(encode);i++){
			if(encode[i]<65)
				cout<<(char)(encode[i]+64);
			else
				cout<<encode[i];
		}
		cout<<endl;
#endif
		if(exists(encode,0,strlen(encode)-1))
		{
			fout<<1<<" "<<cnum<<endl;
			long end=clock();
			cout<<end-begintime<<endl;
			//system("pause");
			exit(0);
		}
	}
	if((strlen(encode)-orilen)%3!=0){
		return ;
	}
	int cpos=-1,opos=-1,wpos=-1;
	for(int j=0;j<strlen(encode);j++){
		if(encode[j]=='O'){
			opos=j;
			for(int i=0;i<opos;i++){
				if(encode[i]=='C'){
					cpos=i;
					bool flag=true;
					if(cpos>opos)
						continue;
					if(cpos>0&&!isencodechar(encode[cpos-1])&&!(isencodechar(encode[opos+1]))){
						tmp[0]=encode[cpos-1];
						tmp[1]=encode[opos+1];
						tmp[2]=0;
						flag=exists(tmp,0,1);
						if(!flag)
							continue;
					}
					for(int s=strlen(encode)-1;s>opos;s--){
						if(encode[s]=='W'){
							wpos=s;
							if((cpos<opos&&opos<wpos)){
								bool flag=true;
								 
								if(cpos>0&&!isencodechar(encode[wpos-1])&&!(isencodechar(encode[cpos+1]))){
									tmp[0]=encode[wpos-1];
									tmp[1]=encode[cpos+1];
									tmp[2]=0;
									flag=exists(tmp,0,1);
									if(!flag)
										continue;
								}
								if(cpos>0&&!isencodechar(encode[opos-1])&&!(isencodechar(encode[wpos+1]))){
									tmp[0]=encode[opos-1];
									tmp[1]=encode[wpos+1];
									tmp[2]=0;
									flag=exists(tmp,0,1);
									if(!flag)
										continue;
								}
								int ptr=0;
								//cout<<cpos<<" "<<opos<<" "<<wpos<<"\t";
								for(int f=0;f<cpos;f++){
									decode[ptr++]=encode[f];
								}
								for(int f=opos+1;f<wpos;f++)
									decode[ptr++]=encode[f];
								for(int f=cpos+1;f<opos;f++)
									decode[ptr++]=encode[f];
								for(int f=wpos+1;f<strlen(encode);f++)
									decode[ptr++]=encode[f];
								decode[ptr]=0;
								search(decode,depth-1);
							}
						}


					}
				}
			}
		}
	}
	delete []decode;
} 

    Test 1: TEST OK [0.000 secs, 3076 KB]
   Test 2: TEST OK [0.000 secs, 3076 KB]
   Test 3: TEST OK [0.000 secs, 3076 KB]
   Test 4: TEST OK [0.000 secs, 3076 KB]
   Test 5: TEST OK [0.000 secs, 3076 KB]
   Test 6: TEST OK [0.081 secs, 3076 KB]
   Test 7: TEST OK [0.000 secs, 3076 KB]
   Test 8: TEST OK [0.054 secs, 3076 KB]
   Test 9: TEST OK [0.243 secs, 3076 KB]
   Test 10: TEST OK [0.243 secs, 3076 KB]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章