dfs搜索+剪枝
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 char fin[48]; 5 char a[80]; 6 int atop=0; 7 char c; 8 int cnt; 9 int vis[1000010]; 10 struct point 11 { 12 char s[80]; 13 int cur; 14 } p; 15 int init(char *str) 16 { 17 int len=strlen(str); 18 int left=0; 19 int right; 20 while(left<len) 21 { 22 while(str[left]==‘C‘||str[left]==‘O‘||str[left]==‘W‘) 23 { 24 left++; 25 } 26 if(left==atop) return 0; 27 right=left; 28 while(str[right]!=‘C‘&&str[right]!=‘O‘&&str[right]!=‘W‘&&str[right]) 29 { 30 right++; 31 } 32 if(right-left>47) return 1; 33 int sign=0; 34 for(int i=0; i<48-right+left; i++) 35 { 36 int sign1=1; 37 for(int j=left; j<right; j++) 38 { 39 if(str[j]!=fin[i+j-left]) 40 { 41 sign1=0; 42 break; 43 } 44 } 45 if(sign1) 46 { 47 sign=1; 48 break; 49 } 50 } 51 if(sign==0) return 1; 52 left=right; 53 } 54 return 0; 55 } 56 unsigned int BKDRHash(char *str) 57 { 58 unsigned int seed = 131; 59 unsigned int hash = 0; 60 while (*str) 61 { 62 hash = hash * seed + (*str++); 63 } 64 return hash%1000007; 65 } 66 int dfs(point pp) 67 { 68 if(pp.cur==cnt) 69 { 70 if(strcmp(pp.s,fin)==0) return 1; 71 else return 0; 72 } 73 int len=47+(cnt-pp.cur)*3; 74 int c[20],o[20],w[20]; 75 int ctop=0,otop=0,wtop=0; 76 for(int i=0; i<len; i++) 77 { 78 if(pp.s[i]==‘C‘) c[ctop++]=i; 79 if(pp.s[i]==‘O‘) o[otop++]=i; 80 if(pp.s[i]==‘W‘) w[wtop++]=i; 81 } 82 if(w[wtop-1]<o[otop-1]||w[wtop-1]<c[ctop-1]) return 0; 83 if(c[0]>o[0]||c[0]>w[0]) return 0; 84 for(int i=0;i<c[0];i++) 85 { 86 if(pp.s[i]!=fin[i]) return 0; 87 } 88 if(ctop!=otop||ctop!=wtop) return 0; 89 if(init(pp.s)) return 0; 90 for(int i=0; i<otop; i++) 91 { 92 for(int j=wtop-1; w[j]>o[i]&&j>=0; j--) 93 { 94 for(int k=0; c[k]<o[i]&&k<ctop; k++) 95 { 96 int cwei=c[k]; 97 int owei=o[i]; 98 int wwei=w[j]; 99 point p1; 100 int p1top=0; 101 p1.cur=pp.cur+1; 102 for(int i=0; i<cwei; i++) 103 { 104 p1.s[p1top++]=pp.s[i]; 105 } 106 for(int i=owei+1; i<wwei; i++) 107 { 108 p1.s[p1top++]=pp.s[i]; 109 } 110 for(int i=cwei+1; i<owei; i++) 111 { 112 p1.s[p1top++]=pp.s[i]; 113 } 114 for(int i=wwei+1; i<len; i++) 115 { 116 p1.s[p1top++]=pp.s[i]; 117 } 118 p1.s[p1top]=0; 119 int temp=BKDRHash(p1.s); 120 if(vis[temp]==0) 121 { 122 vis[temp]=1; 123 if(dfs(p1)) return 1; 124 } 125 } 126 } 127 } 128 return 0; 129 } 130 int main() 131 { 132 memset(vis,0,sizeof(vis)); 133 strcpy(fin,"Begin the Escape execution at the Break of Dawn"); 134 while(scanf("%c",&c)!=EOF) 135 { 136 if(c!=‘\n‘) 137 a[atop++]=c; 138 } 139 a[atop]=0; 140 if(atop<47||(atop-47)%3) 141 { 142 printf("0 0\n"); 143 } 144 else 145 { 146 cnt=(atop-47)/3; 147 strcpy(p.s,a); 148 p.cur=0; 149 if(dfs(p)) printf("1 %d\n",cnt); 150 else printf("0 0\n"); 151 } 152 return 0; 153 }
剪枝: (from nocow)
- 由于添加的COW是一起的,因此给出的字符串的字符个数应该等于47(目标字符串的长度)+3*k。如果不满足就可直接判断无解。
- 除了COW三个字符外,其他的字符的个数应该和目标串相一致。如果不一致也可直接判断无解。
- 搜索中间肯定会出现很多相同的情况,因此需要开一个hash来记录搜索到过哪些字符串,每搜索到一个字符串,就判重。如果重复直接剪枝。这里的字符串的hash函数可以采用ELFhash,但由于ELFhash的数值太大,所以用函数值对一个大质数(我用的是99991)取余,这样可以避免hash开得太大,同时又可以减少冲突。
- 对搜索到的字符串,设不包含COW的最长前缀为n前缀(同样也可以定义n后缀),那么如果n前缀不等于目标串的长度相同的前缀,那么当前字符串一定无解,剪枝。N后缀也可采取相同的判断方法。
- 一个有解的字符串中,COW三个字母最早出现的应该是C,最后出现的应该是W,如果不满足则剪枝。
- 当前字符串中任意两个相邻的COW字母中间所夹的字符串一定在目标串中出现过。如果不符合可立即剪枝。
- 需要优化搜索顺序。经过试验我们可以发现,O的位置对于整个COW至关重要。可以说,O的位置决定了整个串是否会有解。因此,我们在搜索时,应该先枚举O的位置,然后再枚举C和W的位置。其中W要倒序枚举。这样比依次枚举COW至少要快20~30倍。
- 在判断当前串的子串是否包含在目标串中的时候,可以先做一个预处理:记录每一个字母曾经出现过的位置,然后可以直接枚举子串的第一个字母的位置。这样比用pos要快2倍左右。
时间: 2024-11-03 20:46:46