字符串壓縮問題——程序員解法

  在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. 將ss[i]前面的字符——一共i個,copy到st中:strncpy( st , ss , i );
  2. 根據ss[i],ss[i+1]求出替換字符,並寫到st[i]中。
  3. 將ss中ss[i+1]之後的部分copy到st中從st[i+1]開始的位置。

  至此,問題解決,所有代碼完成。下面是完整的代碼。

 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 }

補記:
萬倉一黍 網友就這個問題給出了一個完全的數學證實 《算法:字符串消除問題的數學證實》 ,根據其結論能夠獲得更簡潔的算法。

另,曉得飛天千秋雪 網友應邀也給出了一個漂亮的證實——《字符串壓縮問題》。

相關文章
相關標籤/搜索