回文串问题
有关回文串的问题,可能涉及到判断一个字符串是否是回文串、求最大回文子串长度或者求至少添加多少个字符使得输入字符串称为回文串。这些问题我都已经解决,为了使用方便,我把这些实现代码都粘出来了。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 /** 6 * @brief 检测字符串是否为回文字符串,如果是,返回1;否则,返回0 7 */ 8 int isPalindrome(char s[], int len){ 9 int i, mid = len / 2; 10 11 if (len == 1) return 1; 12 13 for (i = 0; i < mid; i++){ 14 if (s[i] != s[len - i - 1]) 15 return 0; 16 } 17 return 1; 18 } 19 20 //穷举策略:O(n^3) 21 int getMaxPalindrome1(char s[], int len, int *start) 22 { 23 printf("O(N^3) method.\n"); 24 //offset是子串起始相对于输入字符串起始的偏移量,j是子串长度 25 int offset = 0, j = len; 26 int maxLen = 0;//存储回文子串的最大长度 27 28 if (len <= 1) return -1;//出错 29 30 while (offset < len){//子串起始逐个后移 31 j = len - offset;//j初始化为可获得的最大子串长度 32 while (j > 1){ 33 if (isPalindrome(s+offset, j)){ 34 if (maxLen < j){ 35 maxLen = j; 36 *start = offset;//记录最大子串的偏移 37 } 38 } 39 j--;//缩短子串 40 } 41 offset++;//偏移量后移 42 } 43 44 return maxLen; 45 } 46 47 //此算法仍然可以继续优化,在知道maxLen值的情况下,我们可以直接 48 //寻找以offset为中心比maxLen大一个的长度来初始化left和right 49 //的初始值,算法更加高效. 50 int getMaxPalindrome2(char s[], int len, int *start) 51 { 52 printf("O(N^2) method.\n"); 53 54 //奇数长度子串和偶数长度子串的最大回文长度 55 int offset = 0, maxLen = 0, oddLen, evenLen; 56 int left, right;//分别指向子串的左右边界 57 58 if (len <= 1) return -1; 59 60 while (offset < len){ 61 //奇数长度子串的情况 62 left = offset - 1; 63 right = offset + 1; 64 oddLen = 1;//初始化最大奇数回文长度为1 65 //while (left >= 0 && right < len && isPalindrome(s+left, right-left+1)){ 66 while (left >= 0 && right < len && s[left] == s[right]){ 67 left--; 68 right++; 69 oddLen += 2; 70 } 71 if (oddLen > maxLen){ 72 maxLen = oddLen; 73 *start = ++left; 74 } 75 76 //偶数长度子串的情况 77 left = offset; 78 right = offset + 1; 79 evenLen = 0;//初始化最大偶数回文长度为0 80 //while (left >= 0 && right < len && isPalindrome(s+left, right-left+1)){ 81 while (left >= 0 && right < len && s[left] == s[right]){ 82 left--; 83 right++; 84 evenLen += 2; 85 } 86 if (evenLen > maxLen){ 87 maxLen = evenLen; 88 *start = ++left; 89 } 90 91 //偏移量自加 92 offset++; 93 } 94 95 return maxLen; 96 } 97 98 // Manacher算法实现 99 // 对原字符串进行预处理 100 // srcstr[] 原字符数组 head:新串起始标志 separator:字符之间的分隔符 101 char* prepare(char srcstr[], int len, char head, char separator) 102 { 103 int i, length = len; 104 char *newstr = (char*)malloc(2*length + 3); 105 newstr[0] = head; 106 for (i = 0; i < length; i++){ 107 newstr[2*i+1] = separator; 108 newstr[2*i+2] = srcstr[i]; 109 } 110 newstr[2*length+1] = separator; 111 newstr[2*length+2] = '\0';//添加结尾标志 112 113 return newstr;//返回字符串起始地址 114 } 115 116 int getMaxPalindrome(char s[], int len, int *start) 117 { 118 char* newstr = prepare(s, len, '$', '#');//预处理 119 int maxLen = 0, length = strlen(newstr); //新串长度 120 int i, mx = 0, pi = 1;//最长回文右边界mx和对称中心索引pi 121 int* p = (int*)malloc(length * sizeof(int));//辅助数组 122 123 printf("O(N) method.\n"); 124 125 p[0] = 0;//头标志$不统计回文串长度 126 for (i = 1; i < length; i++){ 127 if (mx > i){//如果p[i]在最长回文串的范围内 128 p[i] = ((mx - i) < p[2*pi - i]) ? (mx - i) : p[2*pi - i]; 129 }else{ 130 p[i] = 1; 131 } 132 133 while (newstr[i - p[i]] == newstr[i + p[i]] && i - p[i] > 0 && i + p[i] < length){ 134 p[i]++; 135 } 136 137 if (i + p[i] > mx){//如果新的最长回文串超出mx边界 138 mx = i + p[i]; 139 pi = i; 140 } 141 } 142 //寻找最大回文长度 143 for (i = 1; i < length; i++) 144 { 145 if (p[i] > maxLen){ 146 maxLen = p[i]; 147 *start = (i-p[i])/2;//计算原串中最长回文子串起始索引 148 } 149 } 150 151 free(newstr); 152 free(p); 153 154 return maxLen - 1; 155 } 156 157 //返回输入字符串应该添加至少多少个字符方可构成回文串 158 int toPalindrome(char s[], int len) 159 { 160 char* re = (char*)malloc(len);//对s置逆 161 int cnt, i, j, temp, maxComm = 0;//两字符串的公共子串最大值 162 163 for (i = 0; i < len; i++){ 164 re[len-1-i] = s[i]; 165 } 166 //计算最长公共子串 167 for (cnt = 0; cnt < len; cnt++){ 168 i = cnt; j = 0; 169 temp = 0; 170 while(i < len){ 171 if (s[i] == re[j]){ 172 temp++; 173 i++; j++; 174 }else{ 175 if (temp > maxComm) 176 maxComm = temp; 177 temp = 0; 178 j++; 179 } 180 181 if (j >= len){//如果其中一个字符串已经遍历一遍 182 if (temp > maxComm) 183 maxComm = temp; 184 temp = 0; 185 j = 0; 186 i++; 187 } 188 } 189 } 190 191 return len - maxComm; 192 } 193 194 // for Test 195 int main(void) 196 { 197 char str[100]; 198 char* newstr = NULL; 199 int i, len, start = 0; 200 201 fgets(str, sizeof(str), stdin); 202 len = (int)strlen(str); 203 if (str[len-1] == '\n'){ 204 str[len-1] = '\0'; 205 --len; 206 } 207 printf("Get: %s of size %d\nAnswer: ", str, len); 208 209 #if 0 210 newstr = prepare(str, len, '$', '#'); 211 printf("new string: %s\n", newstr); 212 printf("new length: %d\n", strlen(newstr)); 213 free(newstr); newstr = NULL; 214 #endif 215 216 #if 1 217 printf(" Add %d\n", toPalindrome(str, len)); 218 #endif 219 220 #if 0 221 if (isPalindrome(str, len)){ 222 printf("YES\n"); 223 }else{ 224 printf("No\n"); 225 } 226 //最大回文子串 227 len = getMaxPalindrome(str, len, &start); 228 if (len > 1){ 229 printf("Max Palindrome String Length: %d\n", len); 230 for (i = 0; i < len; i++) 231 printf("%c", str[start+i]); 232 printf("\n"); 233 } 234 #endif 235 236 return 0; 237 }