在http://www.cnblogs.com/bestDavid/p/Stringeliminate.html看到了一個有趣的小題:html
給定一個字符串,僅由a,b,c 3種小寫字母組成。當出現連續兩個不一樣的字母時,你能夠用另一個字母替換它。
若有ab或ba連續出現,你把它們替換爲字母c;
有ac或ca連續出現時,你能夠把它們替換爲字母b;
有bc或cb連續出現時,你能夠把它們替換爲字母a。
你能夠不斷反覆按照這個規則進行替換,你的目標是使得最終結果所獲得的字符串儘量短,求最終結果的最短長度。
輸入:字符串。長度不超過200,僅由abc三種小寫字母組成。
輸出:按照上述規則不斷消除替換,所獲得的字符串最短的長度。
例如:
輸入cab,輸出2。由於咱們能夠把它變爲bb或者變爲cc。
輸入bcab,輸出1。儘管咱們能夠把它變爲aab -> ac -> b,也能夠把它變爲bbb,但由於前者長度更短,因此輸出1。程序員
這個題目原博文中是用「數學」的方法給出解答的,但其推理證實過程(包括引文給出的證實過程)並不嚴謹,結論依據不足。因此這裏用程序員的思考方法給出解答。算法
程序員的方法無非就是老老實實地進行替換。對每一種能夠替換的情形都進行替換。例如對bcab,能夠替換爲aab,bbb和bcc。題中提到「能夠不斷反覆按照這個規則進行替換」,因此上面的各個狀況又能夠進一步
aab:
aab->ac->b 長度爲1.
bbb:
無不一樣字母,沒法進一步替換。長度爲3
bcc:
bcc->ac->b 長度爲1
綜上,對bcab進行不斷變換,所獲得的字符串的最短長度爲1。
所以,很容易給出代碼整體結構。 數組
1 #include <stdio.h> 2 3 #define MAX 200 4 5 typedef char String[ MAX + 1 ]; 6 7 #define N(x) N_(x) 8 #define N_(x) #x 9 10 void input( char * ); 11 unsigned min_len( char * ); 12 13 int main( void ) 14 { 15 String str; 16 17 input( str ); 18 19 printf("替換最終結果的最短長度爲%u。\n" , min_len( str ) ); 20 21 return 0; 22 } 23 24 unsigned min_len( char *s ) 25 { 26 27 } 28 29 void input( char *s ) 30 { 31 puts("輸入字符串"); 32 scanf("%"N(MAX)"[abc]s",s); 33 puts("要肯定長度的字符串爲:"); 34 puts( s ); 35 }
其中宏 MAX 爲字符串初始最大長度,爲了給字符串結尾的\0留出空間,存儲字符串的字符數組的最大尺寸爲 MAX + 1 。因爲這種類型在整個代碼中頻繁用到,因此用 typedef 爲其取了一個統一的名字。
函數input()輸入字符串因爲字符串由a、b、c三種字母組成,因此禁止輸入其餘字符這就是 scanf("%"N(MAX)"[abc]s",s); 中[abc]的目的。又考慮到防止數組輸入越界,所以爲輸入增長了寬度限制——N(MAX),宏展開後的結果爲"200"。因此"%"N(MAX)"[abc]s"就是"%200[abc]s"。函數
下面考慮min_len( char *s )函數。基本思路就是先求出s的初始長度spa
int len = strlen( s );
而後從頭到尾檢查是否有相鄰的相異字符,若有則替換:code
reduce( newstr , s , i1 );
這樣將獲得一個新的字符串newstr。設新字符串的長度爲htm
int newlen ;
新字符串最終長度顯然仍然能夠用min_len()求得:blog
newlen = min_len( newstr );
若是newlen小於len,則將len記爲newlen的值。字符串
if ( newlen < len ) { len = newlen ; }
這樣返回最後的len就是變換後的最短長度。min_len函數完整代碼以下:
1 unsigned min_len( char *s ) 2 { 3 int len = strlen( s ); 4 int i = 0 ; 5 while ( s[i] != '\0' && s[i+1] != '\0' ) 6 { 7 if ( s[i] != s[i+1] ) 8 { 9 String newstr ; 10 unsigned newlen ; 11 12 reduce( newstr , s , i ); 13 newlen = min_len( newstr ); 14 if ( newlen < len ) 15 { 16 len = newlen ; 17 } 18 } 19 i ++ ; 20 } 21 return len; 22 }
再來看一下reduce()函數。完成三件事:
至此,問題解決,所有代碼完成。下面是完整的代碼。
1 #include <stdio.h> 2 #include <string.h> 3 4 #define MAX 200 5 6 typedef char String[ MAX + 1 ]; 7 8 #define N(x) N_(x) 9 #define N_(x) #x 10 11 void input( char * ); 12 unsigned min_len( char * ); 13 void reduce( char * , char * , unsigned ); 14 char turn ( char , char ); 15 16 int main( void ) 17 { 18 String str; 19 20 input( str ); 21 22 printf("替換最終結果的最短長度爲%u。\n" , min_len( str ) ); 23 24 return 0; 25 } 26 27 char turn ( char c1 , char c2 ) 28 { 29 return 'a' + 'b' + 'c' - c1 - c2 ; 30 } 31 32 void reduce( char * st , char * ss , unsigned i ) 33 { 34 strncpy( st , ss , i ); 35 st[i] = turn ( ss[i] , ss[i+1] ); 36 strcpy( st + i + 1 , ss + i + 2 ); 37 } 38 39 unsigned min_len( char *s ) 40 { 41 int len = strlen( s ); 42 int i = 0 ; 43 while ( s[i] != '\0' && s[i+1] != '\0' ) 44 { 45 if ( s[i] != s[i+1] ) 46 { 47 String newstr ; 48 unsigned newlen ; 49 50 reduce( newstr , s , i ); 51 newlen = min_len( newstr ); 52 53 if ( newlen < len ) 54 len = newlen ; 55 } 56 i ++ ; 57 } 58 return len; 59 } 60 61 void input( char *s ) 62 { 63 puts("輸入字符串"); 64 scanf("%"N(MAX)"[abc]s",s); 65 66 puts("要肯定長度的字符串爲:"); 67 puts( s ); 68 }
補記:
萬倉一黍 網友就這個問題給出了一個完全的數學證實 《算法:字符串消除問題的數學證實》 ,根據其結論能夠獲得更簡潔的算法。
另,曉得飛天千秋雪 網友應邀也給出了一個漂亮的證實——《字符串壓縮問題》。