[算法 笔记]字符串移位包含问题
问题出自《编程之美》3.1
1 /* 2013.6.25 2 * 问题:给定两个字符串s1和s2,要求判定给定字符串s2是否 3 * 能够通过字符串s1循环移位得到的字符串包含。 4 * 例如: 5 * 1. s1 = AABCD,s2 = CDAA,则返回true; 6 * 2. s1 = ABCD,s2 = ACBD,则返回false 7 */ 8 #include <stdio.h> 9 #include <string.h> 10 #include <stdlib.h> 11 12 enum { true = 1, false = 0 }; 13 14 /* 2013.6.25 15 * 方式1:用s2[0]将字符串s1分为两个部分。 16 * 第一部分:strcmp(s2, s1 + j, strlen(s1)-j) ,其中,j是s2[0] == s1[j] 17 * 第二部分:strcmp(s2 + strlen(s1) - j, s1, strlen(s2) - strlen(s1) - j). 18 * 时间复杂度:O( strlen(s1) * strlen(s2) ), 空间复杂度:O(1) 19 */ 20 int CheckRotStr1( const char *s1, const char *s2 ) 21 { 22 int start = 0; 23 int nlen1, nlen2, tmpLen; 24 25 if ( s1 == NULL || s1[0] == '\0' 26 || s2 == NULL || s2[0] == '\0' ) 27 { 28 printf( "Error for arguments!\n" ); 29 return -1; 30 } 31 32 // 查找s2[0]在s1中的位置 33 nlen1 = strlen( s1 ); 34 nlen2 = strlen( s2 ); 35 while ( start < nlen1 ) 36 { 37 if ( s2[0] == s1[start] ) 38 break; 39 40 ++start; 41 } 42 43 if ( start == nlen1 ) 44 return false; 45 46 tmpLen = nlen1 - start; 47 if ( strncmp( s2, s1 + start, tmpLen ) == 0 ) 48 { 49 if ( tmpLen == nlen2 ) 50 return true; 51 52 start = tmpLen; 53 tmpLen = nlen2 - tmpLen; 54 if ( strncmp( s2 + start, s1, tmpLen ) == 0 ) 55 return true; 56 } 57 58 return false; 59 } 60 61 /* 2013.6.25 62 * 方式2:拷贝一份s1填充到s1后面,然后查找s2是否为s1的子串。 63 * 时间复杂度:O(strlen(s2)*strlen(s1)), 空间复杂度:O(2*strlen(s1)) 64 */ 65 int CheckRotStr2( const char *s1, const char *s2 ) 66 { 67 char *newS1 = NULL; 68 int nlen1, nlen2, start = 0; 69 70 if ( s1 == NULL || s1[0] == '\0' 71 || s2 == NULL || s2[0] == '\0' ) 72 { 73 printf( "Error for arguments!\n" ); 74 return -1; 75 } 76 77 nlen1 = strlen( s1 ); 78 nlen2 = strlen( s2 ); 79 newS1 = (char *) malloc( nlen1 * 2 * sizeof( char ) + 1 ); 80 if ( newS1 == NULL ) 81 { 82 printf( "Error for malloc!\n" ); 83 return -1; 84 } 85 86 // 拷贝字符串s1 87 strncpy( newS1, s1, nlen1 ); 88 strncpy( newS1 + nlen1, s1, nlen1 ); 89 nlen1 <<= 1; 90 newS1[nlen1] = '\0'; 91 92 // 查找s2[0]在newS1中的位置 93 while ( start < nlen1 ) 94 { 95 if ( s2[0] == newS1[start] ) 96 break; 97 98 ++start; 99 } 100 101 if ( start == nlen1 ) 102 return false; 103 104 if ( strncmp( s2, newS1 + start, nlen2 ) == 0 ) 105 { 106 free( newS1 ); 107 return true; 108 } 109 110 free( newS1 ); 111 return false; 112 } 113 114 /* 2013.6.25 115 * 方式3:思路和方式2一样,但是利用摩计算来省略拷贝问题 116 * 时间复杂度:O(strlen(s2)+strlen(s1)), 空间复杂度:O(1) 117 */ 118 int CheckRotStr3( const char* s1, const char *s2 ) 119 { 120 int nlen1, nlen2, start = 0; 121 int end, i = 0; 122 123 if ( s1 == NULL || s1[0] == '\0' 124 || s2 == NULL || s2[0] == '\0' ) 125 { 126 printf( "Error for arguments!\n" ); 127 return -1; 128 } 129 130 nlen1 = strlen( s1 ); 131 nlen2 = strlen( s2 ); 132 133 // 查找s2[0]在newS1中的位置 134 while ( start < nlen1 ) 135 { 136 if ( s2[0] == s1[start] ) 137 break; 138 139 ++start; 140 } 141 142 if ( start == nlen1 ) 143 return false; 144 145 end = start + nlen1; 146 // 需要注意,如果nlen2超过nlen1的长度,则返回false。 147 while ( start < end && i < nlen2 ) 148 { 149 int tmp = start % nlen1; // 利用摩计算消除了拷贝的需求 150 151 if ( s2[i] != s1[tmp] ) 152 break; 153 154 ++start; 155 ++i; 156 } 157 158 if ( i == nlen2 ) 159 return true; 160 161 return false; 162 } 163 164 /* 2013.6.25 165 * 以上三个函数在简单模式下成立。但是还是存在错误。 166 */ 167 168 /* 2013.6.25 169 * 本次采用的是方式2,然后使用KMP来进行子串匹配。 170 * KMP算法来自博客 算法之道 171 * 网址:http://blog.csdn.net/v_july_v/article/details/7041827#comments 172 */ 173 int CheckRotStrOpt( const char *s1, const char *s2) 174 { 175 int nlen1, nlen2; 176 int *overlay_value = NULL; 177 char* newS1 = NULL; 178 int index = 0; 179 int i, j, ret = false; 180 181 if ( s1 == NULL || s1[0] == '\0' 182 || s2 == NULL || s2[0] == '\0' ) 183 { 184 printf( "Error for arguments.\n" ); 185 return -1; 186 } 187 188 nlen1 = strlen( s1 ); 189 nlen2 = strlen( s2 ); 190 191 newS1 = (char *) malloc( nlen1 * 2 * sizeof( char ) + 1 ); 192 if ( newS1 == NULL ) 193 { 194 printf( "Error for malloc!\n" ); 195 return -1; 196 } 197 198 // 拷贝字符串s1 199 strncpy( newS1, s1, nlen1 ); 200 strncpy( newS1 + nlen1, s1, nlen1 ); 201 nlen1 <<= 1; 202 newS1[nlen1] = '\0'; 203 204 overlay_value = (int *) malloc( nlen2 * sizeof(int) ); 205 if ( overlay_value == NULL ) 206 { 207 printf( "Error for malloc.\n" ); 208 free( newS1 ); 209 return -1; 210 } 211 overlay_value[0] = -1; 212 213 // nextArray 214 for ( i = 1; i < nlen2; ++i ) 215 { 216 index = overlay_value[i - 1]; 217 while ( index >= 0 && s2[index + 1] != s2[index] ) 218 { 219 index = overlay_value[index]; 220 } 221 222 if ( s2[index + 1] == s2[index] ) 223 overlay_value[i] = index + 1; 224 else 225 overlay_value[i] = -1; 226 } 227 228 i = 0, j = 0; 229 while ( i < nlen2 && j < nlen1 ) 230 { 231 if ( newS1[j] == s2[i] ) 232 { 233 ++i; 234 ++j; 235 } 236 else if ( i == 0 ) 237 { 238 ++j; 239 } 240 else 241 { 242 i = overlay_value[i - 1] + 1; 243 } 244 } 245 246 if ( i == nlen2 ) 247 ret = true; 248 249 free( newS1 ); 250 free( overlay_value ); 251 252 return ret; 253 } 254 255 int main() 256 { 257 char* str10 = "AABCDA"; 258 char* str11 = "ABCD"; 259 char* str20 = "CDAA"; 260 char* str21 = "ACBD"; 261 int ret = 0; 262 263 ret = CheckRotStrOpt( str10, str20 ); 264 printf( "The result is %d.\n", ret ); 265 266 ret = CheckRotStrOpt( str11, str21 ); 267 printf( "The result is %d.\n", ret ); 268 269 return 0; 270 }