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


  在http://www.cnblogs.com/bestDavid/p/Stringeliminate.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的初始長度

int len = strlen( s );

  然后從頭至尾檢查是否有相鄰的相異字符,如有則替換:

reduce( newstr , s , i1 );

  這樣將得到一個新的字符串newstr。設新字符串的長度為

int newlen ;

  新字符串最終長度顯然仍然可以用min_len()求得:

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 }

補記:
萬倉一黍 網友就這個問題給出了一個徹底的數學證明 《算法:字符串消除問題的數學證明》 ,根據其結論可以得到更簡潔的算法。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM