回文串问题

有关回文串的问题,可能涉及到判断一个字符串是否是回文串、求最大回文子串长度或者求至少添加多少个字符使得输入字符串称为回文串。这些问题我都已经解决,为了使用方便,我把这些实现代码都粘出来了。

  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 }

 

posted @ 2015-05-01 15:52  XiaoManon  阅读(350)  评论(0编辑  收藏  举报